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:
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/images/mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gifbin0 -> 3654 bytes
-rw-r--r--app/assets/images/mailers/ci_pipeline_notif_v1/gitlab-logo.gifbin0 -> 3040 bytes
-rw-r--r--app/assets/images/mailers/ci_pipeline_notif_v1/icon-branch-gray.gifbin0 -> 663 bytes
-rw-r--r--app/assets/images/mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gifbin0 -> 369 bytes
-rw-r--r--app/assets/images/mailers/ci_pipeline_notif_v1/icon-commit-gray.gifbin0 -> 278 bytes
-rw-r--r--app/assets/images/mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gifbin0 -> 1013 bytes
-rw-r--r--app/assets/images/mailers/ci_pipeline_notif_v1/icon-x-red.gifbin0 -> 660 bytes
-rw-r--r--app/assets/javascripts/LabelManager.js115
-rw-r--r--app/assets/javascripts/abuse_reports.js.es61
-rw-r--r--app/assets/javascripts/activities.js40
-rw-r--r--app/assets/javascripts/activities.js.es637
-rw-r--r--app/assets/javascripts/admin.js1
-rw-r--r--app/assets/javascripts/api.js36
-rw-r--r--app/assets/javascripts/application.js204
-rw-r--r--app/assets/javascripts/aside.js1
-rw-r--r--app/assets/javascripts/autosave.js1
-rw-r--r--app/assets/javascripts/awards_handler.js16
-rw-r--r--app/assets/javascripts/behaviors/autosize.js1
-rw-r--r--app/assets/javascripts/behaviors/details_behavior.js6
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js1
-rw-r--r--app/assets/javascripts/behaviors/requires_input.js1
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.js1
-rw-r--r--app/assets/javascripts/blob/blob_ci_yaml.js46
-rw-r--r--app/assets/javascripts/blob/blob_ci_yaml.js.es641
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js1
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selector.js3
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selectors.js1
-rw-r--r--app/assets/javascripts/blob/blob_license_selector.js3
-rw-r--r--app/assets/javascripts/blob/blob_license_selectors.js25
-rw-r--r--app/assets/javascripts/blob/blob_license_selectors.js.es622
-rw-r--r--app/assets/javascripts/blob/template_selector.js100
-rw-r--r--app/assets/javascripts/blob/template_selector.js.es698
-rw-r--r--app/assets/javascripts/blob_edit/blob_edit_bundle.js1
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js20
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js.es627
-rw-r--r--app/assets/javascripts/boards/components/board.js.es641
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.js.es69
-rw-r--r--app/assets/javascripts/boards/components/board_card.js.es634
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js.es61
-rw-r--r--app/assets/javascripts/boards/components/board_list.js.es640
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.js.es660
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js.es653
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js.es6111
-rw-r--r--app/assets/javascripts/boards/filters/due_date_filters.js.es65
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js.es63
-rw-r--r--app/assets/javascripts/boards/models/issue.js.es624
-rw-r--r--app/assets/javascripts/boards/models/label.js.es61
-rw-r--r--app/assets/javascripts/boards/models/list.js.es615
-rw-r--r--app/assets/javascripts/boards/models/milestone.js.es67
-rw-r--r--app/assets/javascripts/boards/models/user.js.es61
-rw-r--r--app/assets/javascripts/boards/services/board_service.js.es619
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js.es615
-rw-r--r--app/assets/javascripts/boards/test_utils/simulate_drag.js1
-rw-r--r--app/assets/javascripts/boards/vue_resource_interceptor.js.es61
-rw-r--r--app/assets/javascripts/breakpoints.js1
-rw-r--r--app/assets/javascripts/broadcast_message.js1
-rw-r--r--app/assets/javascripts/build.js129
-rw-r--r--app/assets/javascripts/build_artifacts.js1
-rw-r--r--app/assets/javascripts/build_variables.js.es61
-rw-r--r--app/assets/javascripts/commit.js1
-rw-r--r--app/assets/javascripts/commit/file.js1
-rw-r--r--app/assets/javascripts/commit/image_file.js (renamed from app/assets/javascripts/commit/image-file.js)1
-rw-r--r--app/assets/javascripts/commits.js1
-rw-r--r--app/assets/javascripts/compare.js4
-rw-r--r--app/assets/javascripts/compare_autocomplete.js.es6 (renamed from app/assets/javascripts/compare_autocomplete.js)19
-rw-r--r--app/assets/javascripts/confirm_danger_modal.js3
-rw-r--r--app/assets/javascripts/copy_to_clipboard.js19
-rw-r--r--app/assets/javascripts/create_label.js.es612
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_code_component.js.es645
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_issue_component.js.es647
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_plan_component.js.es644
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_production_component.js.es647
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_review_component.js.es657
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_staging_component.js.es644
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_test_component.js.es644
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time_component.js.es620
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6125
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_service.js.es641
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es690
-rw-r--r--app/assets/javascripts/cycle_analytics/svg/icon_branch.js.es67
-rw-r--r--app/assets/javascripts/cycle_analytics/svg/icon_build_status.js.es67
-rw-r--r--app/assets/javascripts/cycle_analytics/svg/icon_commit.js.es67
-rw-r--r--app/assets/javascripts/diff.js12
-rw-r--r--app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es615
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_btn.js.es623
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_count.js.es64
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es615
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js.es644
-rw-r--r--app/assets/javascripts/diff_notes/mixins/discussion.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/mixins/namespace.js.es69
-rw-r--r--app/assets/javascripts/diff_notes/models/discussion.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/models/note.js.es61
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js.es627
-rw-r--r--app/assets/javascripts/diff_notes/stores/comments.js.es61
-rw-r--r--app/assets/javascripts/dispatcher.js.es6 (renamed from app/assets/javascripts/dispatcher.js)57
-rw-r--r--app/assets/javascripts/dropzone_input.js1
-rw-r--r--app/assets/javascripts/due_date_select.js107
-rw-r--r--app/assets/javascripts/due_date_select.js.es6179
-rw-r--r--app/assets/javascripts/environments/components/environment.js.es6249
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.js.es667
-rw-r--r--app/assets/javascripts/environments/components/environment_external_url.js.es622
-rw-r--r--app/assets/javascripts/environments/components/environment_item.js.es6497
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.js.es631
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.js.es626
-rw-r--r--app/assets/javascripts/environments/environments_bundle.js.es621
-rw-r--r--app/assets/javascripts/environments/services/environments_service.js.es622
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js.es6131
-rw-r--r--app/assets/javascripts/environments/vue_resource_interceptor.js.es612
-rw-r--r--app/assets/javascripts/extensions/array.js1
-rw-r--r--app/assets/javascripts/extensions/element.js.es69
-rw-r--r--app/assets/javascripts/extensions/jquery.js1
-rw-r--r--app/assets/javascripts/files_comment_button.js1
-rw-r--r--app/assets/javascripts/flash.js1
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.es668
-rw-r--r--app/assets/javascripts/gl_dropdown.js168
-rw-r--r--app/assets/javascripts/gl_field_error.js.es6164
-rw-r--r--app/assets/javascripts/gl_field_errors.js.es649
-rw-r--r--app/assets/javascripts/gl_form.js3
-rw-r--r--app/assets/javascripts/graphs/graphs_bundle.js3
-rw-r--r--app/assets/javascripts/graphs/stat_graph.js1
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors.js1
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_graph.js12
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_util.js1
-rw-r--r--app/assets/javascripts/group_avatar.js1
-rw-r--r--app/assets/javascripts/group_label_subscription.js.es653
-rw-r--r--app/assets/javascripts/groups.js13
-rw-r--r--app/assets/javascripts/groups_select.js9
-rw-r--r--app/assets/javascripts/header.js10
-rw-r--r--app/assets/javascripts/importer_status.js17
-rw-r--r--app/assets/javascripts/issuable.js.es6115
-rw-r--r--app/assets/javascripts/issuable_context.js1
-rw-r--r--app/assets/javascripts/issuable_form.js5
-rw-r--r--app/assets/javascripts/issue.js7
-rw-r--r--app/assets/javascripts/issue_status_select.js1
-rw-r--r--app/assets/javascripts/issues_bulk_assignment.js.es6 (renamed from app/assets/javascripts/issues-bulk-assignment.js)129
-rw-r--r--app/assets/javascripts/label_manager.js.es6107
-rw-r--r--app/assets/javascripts/labels.js1
-rw-r--r--app/assets/javascripts/labels_select.js179
-rw-r--r--app/assets/javascripts/layout_nav.js26
-rw-r--r--app/assets/javascripts/lib/ace.js1
-rw-r--r--app/assets/javascripts/lib/chart.js1
-rw-r--r--app/assets/javascripts/lib/cropper.js1
-rw-r--r--app/assets/javascripts/lib/d3.js1
-rw-r--r--app/assets/javascripts/lib/raphael.js1
-rw-r--r--app/assets/javascripts/lib/utils/animate.js1
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js107
-rw-r--r--app/assets/javascripts/lib/utils/custom_event_polyfill.js.es612
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js102
-rw-r--r--app/assets/javascripts/lib/utils/jquery.timeago.js181
-rw-r--r--app/assets/javascripts/lib/utils/notify.js1
-rw-r--r--app/assets/javascripts/lib/utils/pretty_time.js.es667
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js7
-rw-r--r--app/assets/javascripts/lib/utils/type_utility.js1
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js3
-rw-r--r--app/assets/javascripts/line_highlighter.js1
-rw-r--r--app/assets/javascripts/logo.js3
-rw-r--r--app/assets/javascripts/member_expiration_date.js9
-rw-r--r--app/assets/javascripts/members.js.es638
-rw-r--r--app/assets/javascripts/merge_conflict_data_provider.js.es6341
-rw-r--r--app/assets/javascripts/merge_conflict_resolver.js.es683
-rw-r--r--app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es694
-rw-r--r--app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es613
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es628
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es631
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6436
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es689
-rw-r--r--app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es613
-rw-r--r--app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es619
-rw-r--r--app/assets/javascripts/merge_request.js16
-rw-r--r--app/assets/javascripts/merge_request_tabs.js128
-rw-r--r--app/assets/javascripts/merge_request_widget.js.es6 (renamed from app/assets/javascripts/merge_request_widget.js)94
-rw-r--r--app/assets/javascripts/merged_buttons.js1
-rw-r--r--app/assets/javascripts/milestone.js1
-rw-r--r--app/assets/javascripts/milestone_select.js49
-rw-r--r--app/assets/javascripts/namespace_select.js1
-rw-r--r--app/assets/javascripts/network/branch_graph.js (renamed from app/assets/javascripts/network/branch-graph.js)1
-rw-r--r--app/assets/javascripts/network/network.js1
-rw-r--r--app/assets/javascripts/network/network_bundle.js5
-rw-r--r--app/assets/javascripts/new_branch_form.js1
-rw-r--r--app/assets/javascripts/new_commit_form.js1
-rw-r--r--app/assets/javascripts/notes.js85
-rw-r--r--app/assets/javascripts/notifications_dropdown.js1
-rw-r--r--app/assets/javascripts/notifications_form.js1
-rw-r--r--app/assets/javascripts/pager.js63
-rw-r--r--app/assets/javascripts/pager.js.es673
-rw-r--r--app/assets/javascripts/pipeline.js.es615
-rw-r--r--app/assets/javascripts/pipelines.js.es630
-rw-r--r--app/assets/javascripts/preview_markdown.js1
-rw-r--r--app/assets/javascripts/profile/gl_crop.js.es6 (renamed from app/assets/javascripts/profile/gl_crop.js)124
-rw-r--r--app/assets/javascripts/profile/profile.js106
-rw-r--r--app/assets/javascripts/profile/profile.js.es6100
-rw-r--r--app/assets/javascripts/profile/profile_bundle.js1
-rw-r--r--app/assets/javascripts/project.js11
-rw-r--r--app/assets/javascripts/project_avatar.js1
-rw-r--r--app/assets/javascripts/project_find_file.js24
-rw-r--r--app/assets/javascripts/project_fork.js1
-rw-r--r--app/assets/javascripts/project_import.js1
-rw-r--r--app/assets/javascripts/project_label_subscription.js.es653
-rw-r--r--app/assets/javascripts/project_members.js10
-rw-r--r--app/assets/javascripts/project_new.js46
-rw-r--r--app/assets/javascripts/project_select.js5
-rw-r--r--app/assets/javascripts/project_show.js1
-rw-r--r--app/assets/javascripts/projects_list.js1
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6 (renamed from app/assets/javascripts/protected_branch_access_dropdown.js.es6)1
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_create.js.es6 (renamed from app/assets/javascripts/protected_branch_create.js.es6)1
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 (renamed from app/assets/javascripts/protected_branch_dropdown.js.es6)1
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 (renamed from app/assets/javascripts/protected_branch_edit.js.es6)2
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6 (renamed from app/assets/javascripts/protected_branch_edit_list.js.es6)1
-rw-r--r--app/assets/javascripts/protected_branches/protected_branches_bundle.js2
-rw-r--r--app/assets/javascripts/right_sidebar.js27
-rw-r--r--app/assets/javascripts/search.js3
-rw-r--r--app/assets/javascripts/search_autocomplete.js.es6 (renamed from app/assets/javascripts/search_autocomplete.js)188
-rw-r--r--app/assets/javascripts/shortcuts.js1
-rw-r--r--app/assets/javascripts/shortcuts_blob.js1
-rw-r--r--app/assets/javascripts/shortcuts_dashboard_navigation.js1
-rw-r--r--app/assets/javascripts/shortcuts_find_file.js1
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js1
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js1
-rw-r--r--app/assets/javascripts/shortcuts_network.js1
-rw-r--r--app/assets/javascripts/sidebar.js.es617
-rw-r--r--app/assets/javascripts/single_file_diff.js45
-rw-r--r--app/assets/javascripts/smart_interval.js.es6130
-rw-r--r--app/assets/javascripts/snippet/snippet_bundle.js1
-rw-r--r--app/assets/javascripts/snippets_list.js.es61
-rw-r--r--app/assets/javascripts/star.js3
-rw-r--r--app/assets/javascripts/subbable_resource.js.es654
-rw-r--r--app/assets/javascripts/subscription.js33
-rw-r--r--app/assets/javascripts/subscription_select.js1
-rw-r--r--app/assets/javascripts/syntax_highlight.js1
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js.es621
-rw-r--r--app/assets/javascripts/templates/issuable_template_selectors.js.es613
-rw-r--r--app/assets/javascripts/todos.js.es6 (renamed from app/assets/javascripts/todos.js)147
-rw-r--r--app/assets/javascripts/tree.js2
-rw-r--r--app/assets/javascripts/u2f/authenticate.js1
-rw-r--r--app/assets/javascripts/u2f/error.js1
-rw-r--r--app/assets/javascripts/u2f/register.js1
-rw-r--r--app/assets/javascripts/u2f/util.js1
-rw-r--r--app/assets/javascripts/user.js.es616
-rw-r--r--app/assets/javascripts/user_tabs.js188
-rw-r--r--app/assets/javascripts/user_tabs.js.es6158
-rw-r--r--app/assets/javascripts/username_validator.js.es6134
-rw-r--r--app/assets/javascripts/users/calendar.js4
-rw-r--r--app/assets/javascripts/users/users_bundle.js1
-rw-r--r--app/assets/javascripts/users_select.js86
-rw-r--r--app/assets/javascripts/vue_common_component/commit.js.es6176
-rw-r--r--app/assets/javascripts/wikis.js1
-rw-r--r--app/assets/javascripts/zen_mode.js1
-rw-r--r--app/assets/stylesheets/behaviors.scss1
-rw-r--r--app/assets/stylesheets/framework.scss2
-rw-r--r--app/assets/stylesheets/framework/animations.scss7
-rw-r--r--app/assets/stylesheets/framework/avatar.scss72
-rw-r--r--app/assets/stylesheets/framework/blocks.scss51
-rw-r--r--app/assets/stylesheets/framework/buttons.scss52
-rw-r--r--app/assets/stylesheets/framework/callout.scss5
-rw-r--r--app/assets/stylesheets/framework/common.scss67
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss55
-rw-r--r--app/assets/stylesheets/framework/files.scss10
-rw-r--r--app/assets/stylesheets/framework/flash.scss13
-rw-r--r--app/assets/stylesheets/framework/forms.scss86
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss85
-rw-r--r--app/assets/stylesheets/framework/header.scss59
-rw-r--r--app/assets/stylesheets/framework/highlight.scss4
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss6
-rw-r--r--app/assets/stylesheets/framework/layout.scss12
-rw-r--r--app/assets/stylesheets/framework/lists.scss29
-rw-r--r--app/assets/stylesheets/framework/logo.scss85
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss27
-rw-r--r--app/assets/stylesheets/framework/mixins.scss19
-rw-r--r--app/assets/stylesheets/framework/mobile.scss16
-rw-r--r--app/assets/stylesheets/framework/modal.scss2
-rw-r--r--app/assets/stylesheets/framework/nav.scss27
-rw-r--r--app/assets/stylesheets/framework/page-header.scss67
-rw-r--r--app/assets/stylesheets/framework/pagination.scss62
-rw-r--r--app/assets/stylesheets/framework/panels.scss5
-rw-r--r--app/assets/stylesheets/framework/selects.scss39
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss33
-rw-r--r--app/assets/stylesheets/framework/tables.scss3
-rw-r--r--app/assets/stylesheets/framework/timeline.scss3
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap.scss13
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap_variables.scss122
-rw-r--r--app/assets/stylesheets/framework/typography.scss82
-rw-r--r--app/assets/stylesheets/framework/variables.scss65
-rw-r--r--app/assets/stylesheets/framework/wells.scss45
-rw-r--r--app/assets/stylesheets/highlight/dark.scss147
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss137
-rw-r--r--app/assets/stylesheets/highlight/solarized_dark.scss155
-rw-r--r--app/assets/stylesheets/highlight/solarized_light.scss155
-rw-r--r--app/assets/stylesheets/highlight/white.scss15
-rw-r--r--app/assets/stylesheets/mailers/devise.scss10
-rw-r--r--app/assets/stylesheets/mailers/repository_push_email.scss2
-rw-r--r--app/assets/stylesheets/notify.scss10
-rw-r--r--app/assets/stylesheets/pages/admin.scss30
-rw-r--r--app/assets/stylesheets/pages/awards.scss8
-rw-r--r--app/assets/stylesheets/pages/boards.scss197
-rw-r--r--app/assets/stylesheets/pages/builds.scss99
-rw-r--r--app/assets/stylesheets/pages/ci_projects.scss3
-rw-r--r--app/assets/stylesheets/pages/commit.scss84
-rw-r--r--app/assets/stylesheets/pages/commits.scss59
-rw-r--r--app/assets/stylesheets/pages/confirmation.scss10
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss482
-rw-r--r--app/assets/stylesheets/pages/dashboard.scss6
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss9
-rw-r--r--app/assets/stylesheets/pages/diff.scss91
-rw-r--r--app/assets/stylesheets/pages/editor.scss34
-rw-r--r--app/assets/stylesheets/pages/environments.scss107
-rw-r--r--app/assets/stylesheets/pages/errors.scss4
-rw-r--r--app/assets/stylesheets/pages/events.scss12
-rw-r--r--app/assets/stylesheets/pages/groups.scss72
-rw-r--r--app/assets/stylesheets/pages/help.scss8
-rw-r--r--app/assets/stylesheets/pages/icons.scss51
-rw-r--r--app/assets/stylesheets/pages/issuable.scss20
-rw-r--r--app/assets/stylesheets/pages/issues.scss29
-rw-r--r--app/assets/stylesheets/pages/labels.scss43
-rw-r--r--app/assets/stylesheets/pages/lint.scss1
-rw-r--r--app/assets/stylesheets/pages/login.scss188
-rw-r--r--app/assets/stylesheets/pages/members.scss98
-rw-r--r--app/assets/stylesheets/pages/merge_conflicts.scss53
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss140
-rw-r--r--app/assets/stylesheets/pages/milestone.scss136
-rw-r--r--app/assets/stylesheets/pages/note_form.scss7
-rw-r--r--app/assets/stylesheets/pages/notes.scss159
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss302
-rw-r--r--app/assets/stylesheets/pages/profile.scss42
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss4
-rw-r--r--app/assets/stylesheets/pages/projects.scss164
-rw-r--r--app/assets/stylesheets/pages/runners.scss1
-rw-r--r--app/assets/stylesheets/pages/search.scss24
-rw-r--r--app/assets/stylesheets/pages/settings.scss4
-rw-r--r--app/assets/stylesheets/pages/snippets.scss11
-rw-r--r--app/assets/stylesheets/pages/status.scss101
-rw-r--r--app/assets/stylesheets/pages/todos.scss79
-rw-r--r--app/assets/stylesheets/pages/tree.scss27
-rw-r--r--app/assets/stylesheets/pages/ui_dev_kit.scss2
-rw-r--r--app/assets/stylesheets/pages/xterm.scss290
-rw-r--r--app/assets/stylesheets/print.scss27
336 files changed, 10526 insertions, 3806 deletions
diff --git a/app/assets/images/mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif b/app/assets/images/mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif
new file mode 100644
index 00000000000..3f4ef31947b
--- /dev/null
+++ b/app/assets/images/mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif
Binary files differ
diff --git a/app/assets/images/mailers/ci_pipeline_notif_v1/gitlab-logo.gif b/app/assets/images/mailers/ci_pipeline_notif_v1/gitlab-logo.gif
new file mode 100644
index 00000000000..387628f831c
--- /dev/null
+++ b/app/assets/images/mailers/ci_pipeline_notif_v1/gitlab-logo.gif
Binary files differ
diff --git a/app/assets/images/mailers/ci_pipeline_notif_v1/icon-branch-gray.gif b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-branch-gray.gif
new file mode 100644
index 00000000000..5f8f8ca143c
--- /dev/null
+++ b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-branch-gray.gif
Binary files differ
diff --git a/app/assets/images/mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif
new file mode 100644
index 00000000000..27a55b1d61f
--- /dev/null
+++ b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif
Binary files differ
diff --git a/app/assets/images/mailers/ci_pipeline_notif_v1/icon-commit-gray.gif b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-commit-gray.gif
new file mode 100644
index 00000000000..8fe3281d2f6
--- /dev/null
+++ b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-commit-gray.gif
Binary files differ
diff --git a/app/assets/images/mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif
new file mode 100644
index 00000000000..4260e312929
--- /dev/null
+++ b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif
Binary files differ
diff --git a/app/assets/images/mailers/ci_pipeline_notif_v1/icon-x-red.gif b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-x-red.gif
new file mode 100644
index 00000000000..6de166ce0a2
--- /dev/null
+++ b/app/assets/images/mailers/ci_pipeline_notif_v1/icon-x-red.gif
Binary files differ
diff --git a/app/assets/javascripts/LabelManager.js b/app/assets/javascripts/LabelManager.js
deleted file mode 100644
index d4a4c7abaa1..00000000000
--- a/app/assets/javascripts/LabelManager.js
+++ /dev/null
@@ -1,115 +0,0 @@
-(function() {
- this.LabelManager = (function() {
- LabelManager.prototype.errorMessage = 'Unable to update label prioritization at this time';
-
- function LabelManager(opts) {
- // Defaults
- var ref, ref1, ref2;
- if (opts == null) {
- opts = {};
- }
- this.togglePriorityButton = (ref = opts.togglePriorityButton) != null ? ref : $('.js-toggle-priority'), this.prioritizedLabels = (ref1 = opts.prioritizedLabels) != null ? ref1 : $('.js-prioritized-labels'), this.otherLabels = (ref2 = opts.otherLabels) != null ? ref2 : $('.js-other-labels');
- this.prioritizedLabels.sortable({
- items: 'li',
- placeholder: 'list-placeholder',
- axis: 'y',
- update: this.onPrioritySortUpdate.bind(this)
- });
- this.bindEvents();
- }
-
- LabelManager.prototype.bindEvents = function() {
- return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick);
- };
-
- LabelManager.prototype.onTogglePriorityClick = function(e) {
- var $btn, $label, $tooltip, _this, action;
- e.preventDefault();
- _this = e.data;
- $btn = $(e.currentTarget);
- $label = $("#" + ($btn.data('domId')));
- action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
- // Make sure tooltip will hide
- $tooltip = $("#" + ($btn.find('.has-tooltip:visible').attr('aria-describedby')));
- $tooltip.tooltip('destroy');
- return _this.toggleLabelPriority($label, action);
- };
-
- LabelManager.prototype.toggleLabelPriority = function($label, action, persistState) {
- var $from, $target, _this, url, xhr;
- if (persistState == null) {
- persistState = true;
- }
- _this = this;
- url = $label.find('.js-toggle-priority').data('url');
- $target = this.prioritizedLabels;
- $from = this.otherLabels;
- // Optimistic update
- if (action === 'remove') {
- $target = this.otherLabels;
- $from = this.prioritizedLabels;
- }
- if ($from.find('li').length === 1) {
- $from.find('.empty-message').removeClass('hidden');
- }
- if (!$target.find('li').length) {
- $target.find('.empty-message').addClass('hidden');
- }
- $label.detach().appendTo($target);
- // Return if we are not persisting state
- if (!persistState) {
- return;
- }
- if (action === 'remove') {
- xhr = $.ajax({
- url: url,
- type: 'DELETE'
- });
- // Restore empty message
- if (!$from.find('li').length) {
- $from.find('.empty-message').removeClass('hidden');
- }
- } else {
- xhr = this.savePrioritySort($label, action);
- }
- return xhr.fail(this.rollbackLabelPosition.bind(this, $label, action));
- };
-
- LabelManager.prototype.onPrioritySortUpdate = function() {
- var xhr;
- xhr = this.savePrioritySort();
- return xhr.fail(function() {
- return new Flash(this.errorMessage, 'alert');
- });
- };
-
- LabelManager.prototype.savePrioritySort = function() {
- return $.post({
- url: this.prioritizedLabels.data('url'),
- data: {
- label_ids: this.getSortedLabelsIds()
- }
- });
- };
-
- LabelManager.prototype.rollbackLabelPosition = function($label, originalAction) {
- var action;
- action = originalAction === 'remove' ? 'add' : 'remove';
- this.toggleLabelPriority($label, action, false);
- return new Flash(this.errorMessage, 'alert');
- };
-
- LabelManager.prototype.getSortedLabelsIds = function() {
- var sortedIds;
- sortedIds = [];
- this.prioritizedLabels.find('li').each(function() {
- return sortedIds.push($(this).data('id'));
- });
- return sortedIds;
- };
-
- return LabelManager;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/abuse_reports.js.es6 b/app/assets/javascripts/abuse_reports.js.es6
index 2fe46b9fd06..82e526ae0ef 100644
--- a/app/assets/javascripts/abuse_reports.js.es6
+++ b/app/assets/javascripts/abuse_reports.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
const MAX_MESSAGE_LENGTH = 500;
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message';
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js
deleted file mode 100644
index d5e11e22be5..00000000000
--- a/app/assets/javascripts/activities.js
+++ /dev/null
@@ -1,40 +0,0 @@
-(function() {
- this.Activities = (function() {
- function Activities() {
- Pager.init(20, true, false, this.updateTooltips);
- $(".event-filter-link").on("click", (function(_this) {
- return function(event) {
- event.preventDefault();
- _this.toggleFilter($(event.currentTarget));
- return _this.reloadActivities();
- };
- })(this));
- }
-
- Activities.prototype.updateTooltips = function() {
- return gl.utils.localTimeAgo($('.js-timeago', '.content_list'));
- };
-
- Activities.prototype.reloadActivities = function() {
- $(".content_list").html('');
- return Pager.init(20, true);
- };
-
- Activities.prototype.toggleFilter = function(sender) {
- var event_filters, filter;
- $('.event-filter .active').removeClass("active");
- event_filters = $.cookie("event_filter");
- filter = sender.attr("id").split("_")[0];
- $.cookie("event_filter", (event_filters !== filter ? filter : ""), {
- path: gon.relative_url_root || '/'
- });
- if (event_filters !== filter) {
- return sender.closest('li').toggleClass("active");
- }
- };
-
- return Activities;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/activities.js.es6 b/app/assets/javascripts/activities.js.es6
new file mode 100644
index 00000000000..648cb4d5d85
--- /dev/null
+++ b/app/assets/javascripts/activities.js.es6
@@ -0,0 +1,37 @@
+/* eslint-disable no-param-reassign, class-methods-use-this */
+/* global Pager */
+/* global Cookies */
+
+((global) => {
+ class Activities {
+ constructor() {
+ Pager.init(20, true, false, this.updateTooltips);
+ $('.event-filter-link').on('click', (e) => {
+ e.preventDefault();
+ this.toggleFilter(e.currentTarget);
+ this.reloadActivities();
+ });
+ }
+
+ updateTooltips() {
+ gl.utils.localTimeAgo($('.js-timeago', '.content_list'));
+ }
+
+ reloadActivities() {
+ $('.content_list').html('');
+ Pager.init(20, true, false, this.updateTooltips);
+ }
+
+ toggleFilter(sender) {
+ const $sender = $(sender);
+ const filter = $sender.attr('id').split('_')[0];
+
+ $('.event-filter .active').removeClass('active');
+ Cookies.set('event_filter', filter);
+
+ $sender.closest('li').toggleClass('active');
+ }
+ }
+
+ global.Activities = Activities;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js
index f8460beb5d2..31852e4750c 100644
--- a/app/assets/javascripts/admin.js
+++ b/app/assets/javascripts/admin.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, no-undef, padded-blocks, max-len */
(function() {
this.Admin = (function() {
function Admin() {
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 6df2ecf57a2..1c625e2f2b1 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, no-undef, comma-dangle, prefer-arrow-callback, indent, object-curly-spacing, quote-props, no-param-reassign, padded-blocks, max-len */
(function() {
this.Api = {
groupsPath: "/api/:version/groups.json",
@@ -5,36 +6,30 @@
namespacesPath: "/api/:version/namespaces.json",
groupProjectsPath: "/api/:version/groups/:id/projects.json",
projectsPath: "/api/:version/projects.json?simple=true",
- labelsPath: "/api/:version/projects/:id/labels",
- licensePath: "/api/:version/licenses/:key",
- gitignorePath: "/api/:version/gitignores/:key",
- gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key",
+ labelsPath: "/:namespace_path/:project_path/labels",
+ licensePath: "/api/:version/templates/licenses/:key",
+ gitignorePath: "/api/:version/templates/gitignores/:key",
+ gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
-
group: function(group_id, callback) {
var url = Api.buildUrl(Api.groupPath)
.replace(':id', group_id);
return $.ajax({
url: url,
- data: {
- private_token: gon.api_token
- },
dataType: "json"
}).done(function(group) {
return callback(group);
});
},
// Return groups list. Filtered by query
- // Only active groups retrieved
- groups: function(query, skip_ldap, callback) {
+ groups: function(query, options, callback) {
var url = Api.buildUrl(Api.groupsPath);
return $.ajax({
url: url,
- data: {
- private_token: gon.api_token,
- search: query,
- per_page: 20
- },
+ data: $.extend({
+ search: query,
+ per_page: 20
+ }, options),
dataType: "json"
}).done(function(groups) {
return callback(groups);
@@ -46,7 +41,6 @@
return $.ajax({
url: url,
data: {
- private_token: gon.api_token,
search: query,
per_page: 20
},
@@ -61,7 +55,6 @@
return $.ajax({
url: url,
data: {
- private_token: gon.api_token,
search: query,
order_by: order,
per_page: 20
@@ -71,14 +64,14 @@
return callback(projects);
});
},
- newLabel: function(project_id, data, callback) {
+ newLabel: function(namespace_path, project_path, data, callback) {
var url = Api.buildUrl(Api.labelsPath)
- .replace(':id', project_id);
- data.private_token = gon.api_token;
+ .replace(':namespace_path', namespace_path)
+ .replace(':project_path', project_path);
return $.ajax({
url: url,
type: "POST",
- data: data,
+ data: {'label': data},
dataType: "json"
}).done(function(label) {
return callback(label);
@@ -93,7 +86,6 @@
return $.ajax({
url: url,
data: {
- private_token: gon.api_token,
search: query,
per_page: 20
},
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index c029bf3b5ca..76f3c6506ed 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -1,5 +1,6 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, no-undef, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len */
// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
@@ -11,13 +12,13 @@
/*= require jquery-ui/effect-highlight */
/*= require jquery-ui/sortable */
/*= require jquery_ujs */
-/*= require jquery.cookie */
/*= require jquery.endless-scroll */
/*= require jquery.highlight */
/*= require jquery.waitforimages */
/*= require jquery.atwho */
/*= require jquery.scrollTo */
/*= require jquery.turbolinks */
+/*= require js.cookie */
/*= require turbolinks */
/*= require autosave */
/*= require bootstrap/affix */
@@ -52,125 +53,72 @@
/*= require_directory ./u2f */
/*= require_directory . */
/*= require fuzzaldrin-plus */
+/*= require es6-promise.auto */
-(function() {
- window.slugify = function(text) {
- return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase();
- };
+(function () {
+ document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch);
+ window.addEventListener('hashchange', gl.utils.shiftWindow);
- window.ajaxGet = function(url) {
- return $.ajax({
- type: "GET",
- url: url,
- dataType: "script"
- });
- };
-
- window.split = function(val) {
- return val.split(/,\s*/);
- };
+ // automatically adjust scroll position for hash urls taking the height of the navbar into account
+ // https://github.com/twitter/bootstrap/issues/1768
+ window.adjustScroll = function() {
+ var navbar = document.querySelector('.navbar-gitlab');
+ var subnav = document.querySelector('.layout-nav');
+ var fixedTabs = document.querySelector('.js-tabs-affix');
- window.extractLast = function(term) {
- return split(term).pop();
- };
-
- window.rstrip = function(val) {
- if (val) {
- return val.replace(/\s+$/, '');
- } else {
- return val;
- }
- };
+ adjustment = 0;
+ if (navbar) adjustment -= navbar.offsetHeight;
+ if (subnav) adjustment -= subnav.offsetHeight;
+ if (fixedTabs) adjustment -= fixedTabs.offsetHeight;
- // Disable button if text field is empty
- window.disableButtonIfEmptyField = function(field_selector, button_selector) {
- var closest_submit, field;
- field = $(field_selector);
- closest_submit = field.closest('form').find(button_selector);
- if (rstrip(field.val()) === "") {
- closest_submit.disable();
- }
- return field.on('input', function() {
- if (rstrip($(this).val()) === "") {
- return closest_submit.disable();
- } else {
- return closest_submit.enable();
- }
- });
+ return scrollBy(0, adjustment);
};
- // Disable button if any input field with given selector is empty
- window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
- var closest_submit, updateButtons;
- closest_submit = form.find(button_selector);
- updateButtons = function() {
- var filled;
- filled = true;
- form.find('input').filter(form_selector).each(function() {
- return filled = rstrip($(this).val()) !== "" || !$(this).attr('required');
- });
- if (filled) {
- return closest_submit.enable();
- } else {
- return closest_submit.disable();
- }
- };
- updateButtons();
- return form.keyup(updateButtons);
- };
+ window.addEventListener("hashchange", adjustScroll);
- window.sanitize = function(str) {
- return str.replace(/<(?:.|\n)*?>/gm, '');
- };
-
- window.unbindEvents = function() {
- return $(document).off('scroll');
- };
-
- window.shiftWindow = function() {
- return scrollBy(0, -100);
- };
-
- document.addEventListener("page:fetch", unbindEvents);
-
- window.addEventListener("hashchange", shiftWindow);
-
- window.onload = function() {
+ window.onload = function () {
// Scroll the window to avoid the topnav bar
// https://github.com/twitter/bootstrap/issues/1768
if (location.hash) {
- return setTimeout(shiftWindow, 100);
+ return setTimeout(adjustScroll, 100);
}
};
- $(function() {
- var $body, $document, $sidebarGutterToggle, $window, bootstrapBreakpoint, checkInitialSidebarSize, fitSidebarForSize, flash;
- $document = $(document);
- $window = $(window);
- $body = $('body');
+ $(function () {
+ var $body = $('body');
+ var $document = $(document);
+ var $window = $(window);
+ var $sidebarGutterToggle = $('.js-sidebar-toggle');
+ var $flash = $('.flash-container');
+ var bootstrapBreakpoint = bp.getBreakpointSize();
+ var checkInitialSidebarSize;
+ var fitSidebarForSize;
+
+ // Set the default path for all cookies to GitLab's root directory
+ Cookies.defaults.path = gon.relative_url_root || '/';
+
gl.utils.preventDisabledButtons();
- bootstrapBreakpoint = bp.getBreakpointSize();
- $(".nav-sidebar").niceScroll({
+ $('.nav-sidebar').niceScroll({
cursoropacitymax: '0.4',
cursorcolor: '#FFF',
- cursorborder: "1px solid #FFF"
+ cursorborder: '1px solid #FFF'
});
- $(".js-select-on-focus").on("focusin", function() {
- return $(this).select().one('mouseup', function(e) {
+ $('.js-select-on-focus').on('focusin', function () {
+ return $(this).select().one('mouseup', function (e) {
return e.preventDefault();
});
// Click a .js-select-on-focus field, select the contents
// Prevent a mouseup event from deselecting the input
});
- $('.remove-row').bind('ajax:success', function() {
+ $('.remove-row').bind('ajax:success', function () {
$(this).tooltip('destroy')
.closest('li')
.fadeOut();
});
- $('.js-remove-tr').bind('ajax:before', function() {
+ $('.js-remove-tr').bind('ajax:before', function () {
return $(this).hide();
});
- $('.js-remove-tr').bind('ajax:success', function() {
+ $('.js-remove-tr').bind('ajax:success', function () {
return $(this).closest('tr').fadeOut();
});
$('select.select2').select2({
@@ -178,34 +126,35 @@
// Initialize select2 selects
dropdownAutoWidth: true
});
- $('.js-select2').bind('select2-close', function() {
- return setTimeout((function() {
+ $('.js-select2').bind('select2-close', function () {
+ return setTimeout((function () {
$('.select2-container-active').removeClass('select2-container-active');
return $(':focus').blur();
}), 1);
// Close select2 on escape
});
// Initialize tooltips
+ $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover';
$body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]',
- placement: function(_, el) {
+ placement: function (_, el) {
return $(el).data('placement') || 'bottom';
}
});
- $('.trigger-submit').on('change', function() {
+ $('.trigger-submit').on('change', function () {
return $(this).parents('form').submit();
// Form submitter
});
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
// Flash
- if ((flash = $(".flash-container")).length > 0) {
- flash.click(function() {
+ if ($flash.length > 0) {
+ $flash.click(function () {
return $(this).fadeOut();
});
- flash.show();
+ $flash.show();
}
// Disable form buttons while a form is submitting
- $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) {
+ $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
var buttons;
buttons = $('[type="submit"]', this);
switch (e.type) {
@@ -216,68 +165,62 @@
return buttons.enable();
}
});
- $(document).ajaxError(function(e, xhrObj, xhrSetting, xhrErrorText) {
- var ref;
+ $(document).ajaxError(function (e, xhrObj) {
+ var ref = xhrObj.status;
if (xhrObj.status === 401) {
return new Flash('You need to be logged in.', 'alert');
- } else if ((ref = xhrObj.status) === 404 || ref === 500) {
+ } else if (ref === 404 || ref === 500) {
return new Flash('Something went wrong on our end.', 'alert');
}
});
- $('.account-box').hover(function() {
+ $('.account-box').hover(function () {
// Show/Hide the profile menu when hovering the account box
return $(this).toggleClass('hover');
});
- $document.on('click', '.diff-content .js-show-suppressed-diff', function() {
+ $document.on('click', '.diff-content .js-show-suppressed-diff', function () {
var $container;
$container = $(this).parent();
$container.next('table').show();
return $container.remove();
// Commit show suppressed diff
});
- $('.navbar-toggle').on('click', function() {
+ $('.navbar-toggle').on('click', function () {
$('.header-content .title').toggle();
$('.header-content .header-logo').toggle();
$('.header-content .navbar-collapse').toggle();
return $('.navbar-toggle').toggleClass('active');
});
// Show/hide comments on diff
- $body.on("click", ".js-toggle-diff-comments", function(e) {
+ $body.on('click', '.js-toggle-diff-comments', function (e) {
var $this = $(this);
- $this.toggleClass('active');
var notesHolders = $this.closest('.diff-file').find('.notes_holder');
+ $this.toggleClass('active');
if ($this.hasClass('active')) {
- notesHolders.show();
+ notesHolders.show().find('.hide').show();
} else {
notesHolders.hide();
}
$this.trigger('blur');
return e.preventDefault();
});
- $document.off("click", '.js-confirm-danger');
- $document.on("click", '.js-confirm-danger', function(e) {
- var btn, form, text;
+ $document.off('click', '.js-confirm-danger');
+ $document.on('click', '.js-confirm-danger', function (e) {
+ var btn = $(e.target);
+ var form = btn.closest('form');
+ var text = btn.data('confirm-danger-message');
e.preventDefault();
- btn = $(e.target);
- text = btn.data("confirm-danger-message");
- form = btn.closest("form");
return new ConfirmDangerModal(form, text);
});
- $document.on('click', 'button', function() {
- return $(this).blur();
- });
- $('input[type="search"]').each(function() {
- var $this;
- $this = $(this);
+ $('input[type="search"]').each(function () {
+ var $this = $(this);
$this.attr('value', $this.val());
});
- $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function(e) {
+ $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function () {
var $this;
$this = $(this);
return $this.attr('value', $this.val());
});
- $sidebarGutterToggle = $('.js-sidebar-toggle');
- $document.off('breakpoint:change').on('breakpoint:change', function(e, breakpoint) {
+ $document.off('breakpoint:change').on('breakpoint:change', function (e, breakpoint) {
var $gutterIcon;
if (breakpoint === 'sm' || breakpoint === 'xs') {
$gutterIcon = $sidebarGutterToggle.find('i');
@@ -286,7 +229,7 @@
}
}
});
- fitSidebarForSize = function() {
+ fitSidebarForSize = function () {
var oldBootstrapBreakpoint;
oldBootstrapBreakpoint = bootstrapBreakpoint;
bootstrapBreakpoint = bp.getBreakpointSize();
@@ -294,13 +237,13 @@
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
};
- checkInitialSidebarSize = function() {
+ checkInitialSidebarSize = function () {
bootstrapBreakpoint = bp.getBreakpointSize();
- if (bootstrapBreakpoint === "xs" || "sm") {
+ if (bootstrapBreakpoint === 'xs' || 'sm') {
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
};
- $window.off("resize.app").on("resize.app", function(e) {
+ $window.off('resize.app').on('resize.app', function () {
return fitSidebarForSize();
});
gl.awardsHandler = new AwardsHandler();
@@ -309,8 +252,5 @@
// bind sidebar events
new gl.Sidebar();
-
- // Custom time ago
- gl.utils.shortTimeAgo($('.js-short-timeago'));
});
}).call(this);
diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js
index 7b546e79ee0..9417afc2ea7 100644
--- a/app/assets/javascripts/aside.js
+++ b/app/assets/javascripts/aside.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, padded-blocks, max-len */
(function() {
this.Aside = (function() {
function Aside() {
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js
index a9aec6e8ea4..f45dbe4cbf2 100644
--- a/app/assets/javascripts/autosave.js
+++ b/app/assets/javascripts/autosave.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, quotes, prefer-template, no-var, one-var, no-unused-vars, one-var-declaration-per-line, no-void, consistent-return, no-empty, padded-blocks, max-len */
(function() {
this.Autosave = (function() {
function Autosave(field, key) {
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 0decc6d09e6..f4302e2e9f6 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,6 +1,7 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, no-var, spaced-comment, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-template, quotes, comma-dangle, no-param-reassign, no-void, radix, keyword-spacing, space-before-blocks, brace-style, no-underscore-dangle, no-undef, no-plusplus, no-return-assign, camelcase, padded-blocks, max-len */
(function() {
this.AwardsHandler = (function() {
- const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence
+ var FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence
function AwardsHandler() {
this.aliases = gl.emojiAliases();
$(document).off('click', '.js-add-award').on('click', '.js-add-award', (function(_this) {
@@ -91,7 +92,7 @@
css = {
top: ($addBtn.offset().top + $addBtn.outerHeight()) + "px"
};
- if ((position != null) && position === 'right') {
+ if (position === 'right') {
css.left = (($addBtn.offset().left - $menu.outerWidth()) + 20) + "px";
$menu.addClass('is-aligned-right');
} else {
@@ -322,21 +323,18 @@
var frequentlyUsedEmojis;
frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
frequentlyUsedEmojis.push(emoji);
- return $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), {
- path: gon.relative_url_root || '/',
- expires: 365
- });
+ Cookies.set('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 });
};
AwardsHandler.prototype.getFrequentlyUsedEmojis = function() {
var frequentlyUsedEmojis;
- frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',');
+ frequentlyUsedEmojis = (Cookies.get('frequently_used_emojis') || '').split(',');
return _.compact(_.uniq(frequentlyUsedEmojis));
};
AwardsHandler.prototype.renderFrequentlyUsedBlock = function() {
var emoji, frequentlyUsedEmojis, i, len, ul;
- if ($.cookie('frequently_used_emojis')) {
+ if (Cookies.get('frequently_used_emojis')) {
frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>");
for (i = 0, len = frequentlyUsedEmojis.length; i < len; i++) {
@@ -357,7 +355,7 @@
$('ul.emoji-menu-search, h5.emoji-search').remove();
if (term) {
// Generate a search result block
- h5 = $('<h5>').text('Search results');
+ h5 = $('<h5 class="emoji-search" />').text('Search results');
found_emojis = _this.searchEmojis(term).show();
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis);
$('.emoji-menu-content ul, .emoji-menu-content h5').hide();
diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js
index dc8ae601961..a5d62f881fe 100644
--- a/app/assets/javascripts/behaviors/autosize.js
+++ b/app/assets/javascripts/behaviors/autosize.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, consistent-return, no-undef, padded-blocks, max-len */
/*= require jquery.ba-resize */
/*= require autosize */
diff --git a/app/assets/javascripts/behaviors/details_behavior.js b/app/assets/javascripts/behaviors/details_behavior.js
index 1df681a4816..3998ee9a0a0 100644
--- a/app/assets/javascripts/behaviors/details_behavior.js
+++ b/app/assets/javascripts/behaviors/details_behavior.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, quotes, no-var, vars-on-top, padded-blocks, max-len */
(function() {
$(function() {
$("body").on("click", ".js-details-target", function() {
@@ -14,6 +15,11 @@
return $("body").on("click", ".js-details-expand", function(e) {
$(this).next('.js-details-content').removeClass("hide");
$(this).hide();
+
+ var truncatedItem = $(this).siblings('.js-details-short');
+ if (truncatedItem.length) {
+ truncatedItem.addClass("hide");
+ }
return e.preventDefault();
});
});
diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js
index 54b7360ab41..4edcaa15fe5 100644
--- a/app/assets/javascripts/behaviors/quick_submit.js
+++ b/app/assets/javascripts/behaviors/quick_submit.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-undef, prefer-arrow-callback, camelcase, max-len, consistent-return, quotes, object-shorthand, comma-dangle, padded-blocks, max-len */
// Quick Submit behavior
//
// When a child field of a form with a `js-quick-submit` class receives a
diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js
index 894034bdd54..72362988b2e 100644
--- a/app/assets/javascripts/behaviors/requires_input.js
+++ b/app/assets/javascripts/behaviors/requires_input.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, no-else-return, consistent-return, padded-blocks, max-len */
// Requires Input behavior
//
// When called on a form with input fields with the `required` attribute, the
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js
index a6ce378d67a..6a49715590c 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.js
+++ b/app/assets/javascripts/behaviors/toggler_behavior.js
@@ -1,3 +1,4 @@
+/* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, vars-on-top, no-var, max-len */
(function(w) {
$(function() {
// Toggle button. Show/hide content inside parent container.
diff --git a/app/assets/javascripts/blob/blob_ci_yaml.js b/app/assets/javascripts/blob/blob_ci_yaml.js
deleted file mode 100644
index 68758574967..00000000000
--- a/app/assets/javascripts/blob/blob_ci_yaml.js
+++ /dev/null
@@ -1,46 +0,0 @@
-
-/*= require blob/template_selector */
-
-(function() {
- var extend = function(child, parent) { for (var 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; },
- hasProp = {}.hasOwnProperty;
-
- this.BlobCiYamlSelector = (function(superClass) {
- extend(BlobCiYamlSelector, superClass);
-
- function BlobCiYamlSelector() {
- return BlobCiYamlSelector.__super__.constructor.apply(this, arguments);
- }
-
- BlobCiYamlSelector.prototype.requestFile = function(query) {
- return Api.gitlabCiYml(query.name, this.requestFileSuccess.bind(this));
- };
-
- return BlobCiYamlSelector;
-
- })(TemplateSelector);
-
- this.BlobCiYamlSelectors = (function() {
- function BlobCiYamlSelectors(opts) {
- var ref;
- this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-gitlab-ci-yml-selector'), this.editor = opts.editor;
- this.$dropdowns.each((function(_this) {
- return function(i, dropdown) {
- var $dropdown;
- $dropdown = $(dropdown);
- return new BlobCiYamlSelector({
- pattern: /(.gitlab-ci.yml)/,
- data: $dropdown.data('data'),
- wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
- dropdown: $dropdown,
- editor: _this.editor
- });
- };
- })(this));
- }
-
- return BlobCiYamlSelectors;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/blob/blob_ci_yaml.js.es6 b/app/assets/javascripts/blob/blob_ci_yaml.js.es6
new file mode 100644
index 00000000000..37531aaec9b
--- /dev/null
+++ b/app/assets/javascripts/blob/blob_ci_yaml.js.es6
@@ -0,0 +1,41 @@
+/* eslint-disable */
+/*= require blob/template_selector */
+((global) => {
+
+ class BlobCiYamlSelector extends gl.TemplateSelector {
+ requestFile(query) {
+ return Api.gitlabCiYml(query.name, this.requestFileSuccess.bind(this));
+ }
+
+ requestFileSuccess(file) {
+ return super.requestFileSuccess(file);
+ }
+ }
+
+ global.BlobCiYamlSelector = BlobCiYamlSelector;
+
+ class BlobCiYamlSelectors {
+ constructor({ editor, $dropdowns } = {}) {
+ this.editor = editor;
+ this.$dropdowns = $dropdowns || $('.js-gitlab-ci-yml-selector');
+ this.initSelectors();
+ }
+
+ initSelectors() {
+ const editor = this.editor;
+ this.$dropdowns.each((i, dropdown) => {
+ const $dropdown = $(dropdown);
+ return new BlobCiYamlSelector({
+ editor,
+ pattern: /(.gitlab-ci.yml)/,
+ data: $dropdown.data('data'),
+ wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
+ dropdown: $dropdown
+ });
+ });
+ }
+ }
+
+ global.BlobCiYamlSelectors = BlobCiYamlSelectors;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js
index 8cca1aa9232..e0a2e8ac12e 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, camelcase, no-undef, object-shorthand, quotes, comma-dangle, prefer-arrow-callback, no-unused-vars, prefer-template, no-useless-escape, no-alert, padded-blocks, max-len */
(function() {
this.BlobFileDropzone = (function() {
function BlobFileDropzone(form, method) {
diff --git a/app/assets/javascripts/blob/blob_gitignore_selector.js b/app/assets/javascripts/blob/blob_gitignore_selector.js
index 54a09e919f8..7e8f1062ab3 100644
--- a/app/assets/javascripts/blob/blob_gitignore_selector.js
+++ b/app/assets/javascripts/blob/blob_gitignore_selector.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params, no-undef, padded-blocks, max-len */
/*= require blob/template_selector */
@@ -18,6 +19,6 @@
return BlobGitignoreSelector;
- })(TemplateSelector);
+ })(gl.TemplateSelector);
}).call(this);
diff --git a/app/assets/javascripts/blob/blob_gitignore_selectors.js b/app/assets/javascripts/blob/blob_gitignore_selectors.js
index 4e9500428b2..9a694daa010 100644
--- a/app/assets/javascripts/blob/blob_gitignore_selectors.js
+++ b/app/assets/javascripts/blob/blob_gitignore_selectors.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-cond-assign, no-sequences, no-undef, comma-dangle, padded-blocks, max-len */
(function() {
this.BlobGitignoreSelectors = (function() {
function BlobGitignoreSelectors(opts) {
diff --git a/app/assets/javascripts/blob/blob_license_selector.js b/app/assets/javascripts/blob/blob_license_selector.js
index 9a8ef08f4e5..9a77fe35d55 100644
--- a/app/assets/javascripts/blob/blob_license_selector.js
+++ b/app/assets/javascripts/blob/blob_license_selector.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params, comma-dangle, no-undef, padded-blocks, max-len */
/*= require blob/template_selector */
@@ -23,6 +24,6 @@
return BlobLicenseSelector;
- })(TemplateSelector);
+ })(gl.TemplateSelector);
}).call(this);
diff --git a/app/assets/javascripts/blob/blob_license_selectors.js b/app/assets/javascripts/blob/blob_license_selectors.js
deleted file mode 100644
index 39237705e8d..00000000000
--- a/app/assets/javascripts/blob/blob_license_selectors.js
+++ /dev/null
@@ -1,25 +0,0 @@
-(function() {
- this.BlobLicenseSelectors = (function() {
- function BlobLicenseSelectors(opts) {
- var ref;
- this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-license-selector'), this.editor = opts.editor;
- this.$dropdowns.each((function(_this) {
- return function(i, dropdown) {
- var $dropdown;
- $dropdown = $(dropdown);
- return new BlobLicenseSelector({
- pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
- data: $dropdown.data('data'),
- wrapper: $dropdown.closest('.js-license-selector-wrap'),
- dropdown: $dropdown,
- editor: _this.editor
- });
- };
- })(this));
- }
-
- return BlobLicenseSelectors;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/blob/blob_license_selectors.js.es6 b/app/assets/javascripts/blob/blob_license_selectors.js.es6
new file mode 100644
index 00000000000..adeb8ba1318
--- /dev/null
+++ b/app/assets/javascripts/blob/blob_license_selectors.js.es6
@@ -0,0 +1,22 @@
+/* eslint-disable */
+((global) => {
+ class BlobLicenseSelectors {
+ constructor({ $dropdowns, editor }) {
+ this.$dropdowns = $('.js-license-selector');
+ this.editor = editor;
+ this.$dropdowns.each((i, dropdown) => {
+ const $dropdown = $(dropdown);
+ return new BlobLicenseSelector({
+ editor,
+ pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
+ data: $dropdown.data('data'),
+ wrapper: $dropdown.closest('.js-license-selector-wrap'),
+ dropdown: $dropdown,
+ });
+ });
+ }
+ }
+
+ global.BlobLicenseSelectors = BlobLicenseSelectors;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js
deleted file mode 100644
index 95352164d76..00000000000
--- a/app/assets/javascripts/blob/template_selector.js
+++ /dev/null
@@ -1,100 +0,0 @@
-(function() {
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-
- this.TemplateSelector = (function() {
- function TemplateSelector(opts) {
- var ref;
- if (opts == null) {
- opts = {};
- }
- this.onClick = bind(this.onClick, this);
- this.dropdown = opts.dropdown, this.data = opts.data, this.pattern = opts.pattern, this.wrapper = opts.wrapper, this.editor = opts.editor, this.fileEndpoint = opts.fileEndpoint, this.$input = (ref = opts.$input) != null ? ref : $('#file_name');
- this.dropdownIcon = $('.fa-chevron-down', this.dropdown);
- this.buildDropdown();
- this.bindEvents();
- this.onFilenameUpdate();
-
- this.autosizeUpdateEvent = document.createEvent('Event');
- this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
- }
-
- TemplateSelector.prototype.buildDropdown = function() {
- return this.dropdown.glDropdown({
- data: this.data,
- filterable: true,
- selectable: true,
- toggleLabel: this.toggleLabel,
- search: {
- fields: ['name']
- },
- clicked: this.onClick,
- text: function(item) {
- return item.name;
- }
- });
- };
-
- TemplateSelector.prototype.bindEvents = function() {
- return this.$input.on('keyup blur', (function(_this) {
- return function(e) {
- return _this.onFilenameUpdate();
- };
- })(this));
- };
-
- TemplateSelector.prototype.toggleLabel = function(item) {
- return item.name;
- };
-
- TemplateSelector.prototype.onFilenameUpdate = function() {
- var filenameMatches;
- if (!this.$input.length) {
- return;
- }
- filenameMatches = this.pattern.test(this.$input.val().trim());
- if (!filenameMatches) {
- this.wrapper.addClass('hidden');
- return;
- }
- return this.wrapper.removeClass('hidden');
- };
-
- TemplateSelector.prototype.onClick = function(item, el, e) {
- e.preventDefault();
- return this.requestFile(item);
- };
-
- TemplateSelector.prototype.requestFile = function(item) {
- // This `requestFile` method is an abstract method that should
- // be added by all subclasses.
- };
-
- // To be implemented on the extending class
- // e.g.
- // Api.gitignoreText item.name, @requestFileSuccess.bind(@)
- TemplateSelector.prototype.requestFileSuccess = function(file, skipFocus) {
- this.editor.setValue(file.content, 1);
- if (!skipFocus) this.editor.focus();
-
- if (this.editor instanceof jQuery) {
- this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent);
- }
- };
-
- TemplateSelector.prototype.startLoadingSpinner = function() {
- this.dropdownIcon
- .addClass('fa-spinner fa-spin')
- .removeClass('fa-chevron-down');
- };
-
- TemplateSelector.prototype.stopLoadingSpinner = function() {
- this.dropdownIcon
- .addClass('fa-chevron-down')
- .removeClass('fa-spinner fa-spin');
- };
-
- return TemplateSelector;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/blob/template_selector.js.es6 b/app/assets/javascripts/blob/template_selector.js.es6
new file mode 100644
index 00000000000..5434a19bcec
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selector.js.es6
@@ -0,0 +1,98 @@
+/* eslint-disable */
+((global) => {
+ class TemplateSelector {
+ constructor({ dropdown, data, pattern, wrapper, editor, fileEndpoint, $input } = {}) {
+ this.onClick = this.onClick.bind(this);
+ this.dropdown = dropdown;
+ this.data = data;
+ this.pattern = pattern;
+ this.wrapper = wrapper;
+ this.editor = editor;
+ this.fileEndpoint = fileEndpoint;
+ this.$input = $input || $('#file_name');
+ this.dropdownIcon = $('.fa-chevron-down', this.dropdown);
+ this.buildDropdown();
+ this.bindEvents();
+ this.onFilenameUpdate();
+
+ this.autosizeUpdateEvent = document.createEvent('Event');
+ this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
+ }
+
+ buildDropdown() {
+ return this.dropdown.glDropdown({
+ data: this.data,
+ filterable: true,
+ selectable: true,
+ toggleLabel: this.toggleLabel,
+ search: {
+ fields: ['name']
+ },
+ clicked: this.onClick,
+ text: function(item) {
+ return item.name;
+ }
+ });
+ }
+
+ bindEvents() {
+ return this.$input.on('keyup blur', (e) => this.onFilenameUpdate());
+ }
+
+ toggleLabel(item) {
+ return item.name;
+ }
+
+ onFilenameUpdate() {
+ var filenameMatches;
+ if (!this.$input.length) {
+ return;
+ }
+ filenameMatches = this.pattern.test(this.$input.val().trim());
+ if (!filenameMatches) {
+ this.wrapper.addClass('hidden');
+ return;
+ }
+ return this.wrapper.removeClass('hidden');
+ }
+
+ onClick(item, el, e) {
+ e.preventDefault();
+ return this.requestFile(item);
+ }
+
+ requestFile(item) {
+ // This `requestFile` method is an abstract method that should
+ // be added by all subclasses.
+ }
+
+ // To be implemented on the extending class
+ // e.g.
+ // Api.gitignoreText item.name, @requestFileSuccess.bind(@)
+ requestFileSuccess(file, { skipFocus } = {}) {
+ const oldValue = this.editor.getValue();
+ let newValue = file.content;
+
+ this.editor.setValue(newValue, 1);
+ if (!skipFocus) this.editor.focus();
+
+ if (this.editor instanceof jQuery) {
+ this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent);
+ }
+ }
+
+ startLoadingSpinner() {
+ this.dropdownIcon
+ .addClass('fa-spinner fa-spin')
+ .removeClass('fa-chevron-down');
+ }
+
+ stopLoadingSpinner() {
+ this.dropdownIcon
+ .addClass('fa-chevron-down')
+ .removeClass('fa-spinner fa-spin');
+ }
+ }
+
+ global.TemplateSelector = TemplateSelector;
+ })(window.gl || ( window.gl = {}));
diff --git a/app/assets/javascripts/blob_edit/blob_edit_bundle.js b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
index 2afef43f3d6..b8eb0f60a8e 100644
--- a/app/assets/javascripts/blob_edit/blob_edit_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, vars-on-top, no-unused-vars, no-undef, no-new, padded-blocks, max-len */
/*= require_tree . */
(function() {
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index b846bab0424..0c74aaaa852 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, camelcase, no-param-reassign, no-undef, quotes, prefer-template, no-new, comma-dangle, one-var, one-var-declaration-per-line, prefer-arrow-callback, no-else-return, no-unused-vars, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -22,13 +23,14 @@
// submitted textarea
})(this));
this.initModePanesAndLinks();
- new BlobLicenseSelectors({
+ this.initSoftWrap();
+ new gl.BlobLicenseSelectors({
editor: this.editor
});
new BlobGitignoreSelectors({
editor: this.editor
});
- new BlobCiYamlSelectors({
+ new gl.BlobCiYamlSelectors({
editor: this.editor
});
}
@@ -50,6 +52,7 @@
this.$editModePanes.hide();
currentPane.fadeIn(200);
if (paneId === "#preview") {
+ this.$toggleButton.hide();
return $.post(currentLink.data("preview-url"), {
content: this.editor.getValue()
}, function(response) {
@@ -57,10 +60,23 @@
return currentPane.syntaxHighlight();
});
} else {
+ this.$toggleButton.show();
return this.editor.focus();
}
};
+ EditBlob.prototype.initSoftWrap = function() {
+ this.isSoftWrapped = false;
+ this.$toggleButton = $('.soft-wrap-toggle');
+ this.$toggleButton.on('click', this.toggleSoftWrap.bind(this));
+ };
+
+ EditBlob.prototype.toggleSoftWrap = function(e) {
+ this.isSoftWrapped = !this.isSoftWrapped;
+ this.$toggleButton.toggleClass('soft-wrap-active', this.isSoftWrapped);
+ this.editor.getSession().setUseWrapMode(this.isSoftWrapped);
+ };
+
return EditBlob;
})();
diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6
index 91c12570e09..7ba918a05f8 100644
--- a/app/assets/javascripts/boards/boards_bundle.js.es6
+++ b/app/assets/javascripts/boards/boards_bundle.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require vue
//= require vue-resource
//= require Sortable
@@ -5,7 +6,9 @@
//= require_tree ./stores
//= require_tree ./services
//= require_tree ./mixins
+//= require_tree ./filters
//= require ./components/board
+//= require ./components/board_sidebar
//= require ./components/new_list_dropdown
//= require ./vue_resource_interceptor
@@ -19,23 +22,32 @@ $(() => {
gl.IssueBoardsApp.$destroy(true);
}
+ Store.create();
+
gl.IssueBoardsApp = new Vue({
el: $boardApp,
components: {
- 'board': gl.issueBoards.Board
+ 'board': gl.issueBoards.Board,
+ 'board-sidebar': gl.issueBoards.BoardSidebar
},
data: {
state: Store.state,
loading: true,
endpoint: $boardApp.dataset.endpoint,
+ boardId: $boardApp.dataset.boardId,
disabled: $boardApp.dataset.disabled === 'true',
- issueLinkBase: $boardApp.dataset.issueLinkBase
+ issueLinkBase: $boardApp.dataset.issueLinkBase,
+ detailIssue: Store.detail
+ },
+ computed: {
+ detailIssueVisible () {
+ return Object.keys(this.detailIssue.issue).length;
+ },
},
- init: Store.create.bind(Store),
created () {
- gl.boardService = new BoardService(this.endpoint);
+ gl.boardService = new BoardService(this.endpoint, this.boardId);
},
- ready () {
+ mounted () {
Store.disabled = this.disabled;
gl.boardService.all()
.then((resp) => {
@@ -49,6 +61,8 @@ $(() => {
}
});
+ this.state.lists = _.sortBy(this.state.lists, 'position');
+
Store.addBlankState();
this.loading = false;
});
@@ -59,6 +73,9 @@ $(() => {
el: '#js-boards-seach',
data: {
filters: Store.state.filters
+ },
+ mounted () {
+ gl.issueBoards.newListDropdownInit();
}
});
});
diff --git a/app/assets/javascripts/boards/components/board.js.es6 b/app/assets/javascripts/boards/components/board.js.es6
index 7e86f001f44..31de3b25284 100644
--- a/app/assets/javascripts/boards/components/board.js.es6
+++ b/app/assets/javascripts/boards/components/board.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require ./board_blank_state
//= require ./board_delete
//= require ./board_list
@@ -9,6 +10,7 @@
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.Board = Vue.extend({
+ template: '#js-board-template',
components: {
'board-list': gl.issueBoards.BoardList,
'board-delete': gl.issueBoards.BoardDelete,
@@ -21,7 +23,8 @@
},
data () {
return {
- filters: Store.state.filters
+ detailIssue: Store.detail,
+ filters: Store.state.filters,
};
},
watch: {
@@ -31,9 +34,34 @@
this.list.getIssues(true);
},
deep: true
+ },
+ detailIssue: {
+ handler () {
+ if (!Object.keys(this.detailIssue.issue).length) return;
+
+ const issue = this.list.findIssue(this.detailIssue.issue.id);
+
+ if (issue) {
+ const boardsList = document.querySelectorAll('.boards-list')[0];
+ const right = (this.$el.offsetLeft + this.$el.offsetWidth) - boardsList.offsetWidth;
+ const left = boardsList.scrollLeft - this.$el.offsetLeft;
+
+ if (right - boardsList.scrollLeft > 0) {
+ boardsList.scrollLeft = right;
+ } else if (left > 0) {
+ boardsList.scrollLeft = this.$el.offsetLeft;
+ }
+ }
+ },
+ deep: true
}
},
- ready () {
+ methods: {
+ showNewIssueForm() {
+ this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
+ }
+ },
+ mounted () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({
disabled: this.disabled,
group: 'boards',
@@ -44,13 +72,9 @@
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
const order = this.sortable.toArray(),
- $board = this.$parent.$refs.board[e.oldIndex + 1],
- list = $board.list;
-
- $board.$destroy(true);
+ list = Store.findList('id', parseInt(e.item.dataset.id));
this.$nextTick(() => {
- Store.state.lists.splice(e.newIndex, 0, list);
Store.moveList(list, order);
});
}
@@ -59,8 +83,5 @@
this.sortable = Sortable.create(this.$el.parentNode, options);
},
- beforeDestroy () {
- Store.state.lists.$remove(this.list);
- }
});
})();
diff --git a/app/assets/javascripts/boards/components/board_blank_state.js.es6 b/app/assets/javascripts/boards/components/board_blank_state.js.es6
index 63d72d857d9..691487b272a 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.js.es6
+++ b/app/assets/javascripts/boards/components/board_blank_state.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
const Store = gl.issueBoards.BoardsStore;
@@ -8,10 +9,8 @@
data () {
return {
predefinedLabels: [
- new ListLabel({ title: 'Development', color: '#5CB85C' }),
- new ListLabel({ title: 'Testing', color: '#F0AD4E' }),
- new ListLabel({ title: 'Production', color: '#FF5F00' }),
- new ListLabel({ title: 'Ready', color: '#FF0000' })
+ new ListLabel({ title: 'To Do', color: '#F0AD4E' }),
+ new ListLabel({ title: 'Doing', color: '#5CB85C' })
]
}
},
@@ -31,6 +30,8 @@
});
});
+ Store.state.lists = _.sortBy(Store.state.lists, 'position');
+
// Save the labels
gl.boardService.generateDefaultLists()
.then((resp) => {
diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6
index 4a7cfeaeab2..2299dafd217 100644
--- a/app/assets/javascripts/boards/components/board_card.js.es6
+++ b/app/assets/javascripts/boards/components/board_card.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
const Store = gl.issueBoards.BoardsStore;
@@ -5,6 +6,7 @@
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardCard = Vue.extend({
+ template: '#js-board-list-card',
props: {
list: Object,
issue: Object,
@@ -12,6 +14,17 @@
disabled: Boolean,
index: Number
},
+ data () {
+ return {
+ showDetail: false,
+ detailIssue: Store.detail
+ };
+ },
+ computed: {
+ issueDetailVisible () {
+ return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
+ }
+ },
methods: {
filterByLabel (label, e) {
let labelToggleText = label.title;
@@ -37,6 +50,27 @@
$('.labels-filter .dropdown-toggle-text').text(labelToggleText);
Store.updateFiltersUrl();
+ },
+ mouseDown () {
+ this.showDetail = true;
+ },
+ mouseMove() {
+ this.showDetail = false;
+ },
+ showIssue (e) {
+ const targetTagName = e.target.tagName.toLowerCase();
+
+ if (targetTagName === 'a' || targetTagName === 'button') return;
+
+ if (this.showDetail) {
+ this.showDetail = false;
+
+ if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
+ Store.detail.issue = {};
+ } else {
+ Store.detail.issue = this.issue;
+ }
+ }
}
}
});
diff --git a/app/assets/javascripts/boards/components/board_delete.js.es6 b/app/assets/javascripts/boards/components/board_delete.js.es6
index 34653cd48ef..c45e1926c5c 100644
--- a/app/assets/javascripts/boards/components/board_delete.js.es6
+++ b/app/assets/javascripts/boards/components/board_delete.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
diff --git a/app/assets/javascripts/boards/components/board_list.js.es6 b/app/assets/javascripts/boards/components/board_list.js.es6
index 474805c1437..8e91cbfac75 100644
--- a/app/assets/javascripts/boards/components/board_list.js.es6
+++ b/app/assets/javascripts/boards/components/board_list.js.es6
@@ -1,4 +1,6 @@
+/* eslint-disable */
//= require ./board_card
+//= require ./board_new_issue
(() => {
const Store = gl.issueBoards.BoardsStore;
@@ -7,28 +9,31 @@
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardList = Vue.extend({
+ template: '#js-board-list-template',
components: {
- 'board-card': gl.issueBoards.BoardCard
+ 'board-card': gl.issueBoards.BoardCard,
+ 'board-new-issue': gl.issueBoards.BoardNewIssue
},
props: {
disabled: Boolean,
list: Object,
issues: Array,
loading: Boolean,
- issueLinkBase: String
+ issueLinkBase: String,
},
data () {
return {
scrollOffset: 250,
filters: Store.state.filters,
- showCount: false
+ showCount: false,
+ showIssueForm: false
};
},
watch: {
filters: {
handler () {
this.list.loadingMore = false;
- this.$els.list.scrollTop = 0;
+ this.$refs.list.scrollTop = 0;
},
deep: true
},
@@ -47,15 +52,20 @@
});
}
},
+ computed: {
+ orderedIssues () {
+ return _.sortBy(this.issues, 'priority');
+ },
+ },
methods: {
listHeight () {
- return this.$els.list.getBoundingClientRect().height;
+ return this.$refs.list.getBoundingClientRect().height;
},
scrollHeight () {
- return this.$els.list.scrollHeight;
+ return this.$refs.list.scrollHeight;
},
scrollTop () {
- return this.$els.list.scrollTop + this.listHeight();
+ return this.$refs.list.scrollTop + this.listHeight();
},
loadNextPage () {
const getIssues = this.list.nextPage();
@@ -68,15 +78,16 @@
}
},
},
- ready () {
+ mounted () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({
group: 'issues',
sort: false,
disabled: this.disabled,
- filter: '.board-list-count',
+ filter: '.board-list-count, .is-disabled',
onStart: (e) => {
const card = this.$refs.issue[e.oldIndex];
+ card.showDetail = false;
Store.moving.issue = card.issue;
Store.moving.list = card.list;
@@ -84,16 +95,17 @@
},
onAdd: (e) => {
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue);
+
+ this.$nextTick(() => {
+ e.item.remove();
+ });
},
- onRemove: (e) => {
- this.$refs.issue[e.oldIndex].$destroy(true);
- }
});
- this.sortable = Sortable.create(this.$els.list, options);
+ this.sortable = Sortable.create(this.$refs.list, options);
// Scroll event on list to load more
- this.$els.list.onscroll = () => {
+ this.$refs.list.onscroll = () => {
if ((this.scrollTop() > this.scrollHeight() - this.scrollOffset) && !this.list.loadingMore) {
this.loadNextPage();
}
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js.es6 b/app/assets/javascripts/boards/components/board_new_issue.js.es6
new file mode 100644
index 00000000000..a7989a2ff4c
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_new_issue.js.es6
@@ -0,0 +1,60 @@
+/* eslint-disable */
+(() => {
+ const Store = gl.issueBoards.BoardsStore;
+
+ window.gl = window.gl || {};
+
+ gl.issueBoards.BoardNewIssue = Vue.extend({
+ props: {
+ list: Object,
+ },
+ data() {
+ return {
+ title: '',
+ error: false
+ };
+ },
+ methods: {
+ submit(e) {
+ e.preventDefault();
+ if (this.title.trim() === '') return;
+
+ this.error = false;
+
+ const labels = this.list.label ? [this.list.label] : [];
+ const issue = new ListIssue({
+ title: this.title,
+ labels,
+ subscribed: true
+ });
+
+ this.list.newIssue(issue)
+ .then((data) => {
+ // Need this because our jQuery very kindly disables buttons on ALL form submissions
+ $(this.$refs.submitButton).enable();
+
+ Store.detail.issue = issue;
+ })
+ .catch(() => {
+ // Need this because our jQuery very kindly disables buttons on ALL form submissions
+ $(this.$refs.submitButton).enable();
+
+ // Remove the issue
+ this.list.removeIssue(issue);
+
+ // Show error message
+ this.error = true;
+ });
+
+ this.cancel();
+ },
+ cancel() {
+ this.title = '';
+ this.$parent.showIssueForm = false;
+ }
+ },
+ mounted() {
+ this.$refs.input.focus();
+ },
+ });
+})();
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6
new file mode 100644
index 00000000000..d5cb6164e0b
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6
@@ -0,0 +1,53 @@
+/* eslint-disable */
+(() => {
+ const Store = gl.issueBoards.BoardsStore;
+
+ window.gl = window.gl || {};
+ window.gl.issueBoards = window.gl.issueBoards || {};
+
+ gl.issueBoards.BoardSidebar = Vue.extend({
+ props: {
+ currentUser: Object
+ },
+ data() {
+ return {
+ detail: Store.detail,
+ issue: {}
+ };
+ },
+ computed: {
+ showSidebar () {
+ return Object.keys(this.issue).length;
+ }
+ },
+ watch: {
+ detail: {
+ handler () {
+ this.issue = this.detail.issue;
+ },
+ deep: true
+ },
+ issue () {
+ if (this.showSidebar) {
+ this.$nextTick(() => {
+ $('.right-sidebar').getNiceScroll(0).doScrollTop(0, 0);
+ $('.right-sidebar').getNiceScroll().resize();
+ });
+ }
+ }
+ },
+ methods: {
+ closeSidebar () {
+ this.detail.issue = {};
+ }
+ },
+ mounted () {
+ new IssuableContext(this.currentUser);
+ new MilestoneSelect();
+ new gl.DueDateSelectors();
+ new LabelsSelect();
+ new Sidebar();
+ new Subscription('.subscription');
+ }
+ });
+})();
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js.es6 b/app/assets/javascripts/boards/components/new_list_dropdown.js.es6
index 1a4d8157970..10ce746deb5 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js.es6
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js.es6
@@ -1,54 +1,75 @@
-$(() => {
+/* eslint-disable */
+(() => {
+ window.gl = window.gl || {};
+ window.gl.issueBoards = window.gl.issueBoards || {};
+
const Store = gl.issueBoards.BoardsStore;
- $('.js-new-board-list').each(function () {
- const $this = $(this);
+ $(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,
+ title: label.title,
+ color: label.color
+ }
+ });
+ });
- new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('project-id'));
+ gl.issueBoards.newListDropdownInit = () => {
+ $('.js-new-board-list').each(function () {
+ const $this = $(this);
+ new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path'));
- $this.glDropdown({
- data(term, callback) {
- $.get($this.attr('data-labels'))
- .then((resp) => {
- callback(resp);
- });
- },
- renderRow (label) {
- const active = Store.findList('title', label.title),
- $li = $('<li />'),
- $a = $('<a />', {
- class: (active ? `is-active js-board-list-${active.id}` : ''),
- text: label.title,
- href: '#'
- }),
- $labelColor = $('<span />', {
- class: 'dropdown-label-box',
- style: `background-color: ${label.color}`
- });
+ $this.glDropdown({
+ data(term, callback) {
+ $.get($this.attr('data-labels'))
+ .then((resp) => {
+ callback(resp);
+ });
+ },
+ renderRow (label) {
+ const active = Store.findList('title', label.title),
+ $li = $('<li />'),
+ $a = $('<a />', {
+ class: (active ? `is-active js-board-list-${active.id}` : ''),
+ text: label.title,
+ href: '#'
+ }),
+ $labelColor = $('<span />', {
+ class: 'dropdown-label-box',
+ style: `background-color: ${label.color}`
+ });
- return $li.append($a.prepend($labelColor));
- },
- search: {
- fields: ['title']
- },
- filterable: true,
- selectable: true,
- clicked (label, $el, e) {
- e.preventDefault();
+ return $li.append($a.prepend($labelColor));
+ },
+ search: {
+ fields: ['title']
+ },
+ filterable: true,
+ selectable: true,
+ multiSelect: true,
+ clicked (label, $el, e) {
+ e.preventDefault();
- if (!Store.findList('title', label.title)) {
- Store.new({
- title: label.title,
- position: Store.state.lists.length - 2,
- list_type: 'label',
- label: {
- id: label.id,
+ if (!Store.findList('title', label.title)) {
+ Store.new({
title: label.title,
- color: label.color
- }
- });
+ position: Store.state.lists.length - 2,
+ list_type: 'label',
+ label: {
+ id: label.id,
+ title: label.title,
+ color: label.color
+ }
+ });
+
+ Store.state.lists = _.sortBy(Store.state.lists, 'position');
+ }
}
- }
+ });
});
- });
-});
+ };
+})();
diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 b/app/assets/javascripts/boards/filters/due_date_filters.js.es6
new file mode 100644
index 00000000000..9eceac4eddd
--- /dev/null
+++ b/app/assets/javascripts/boards/filters/due_date_filters.js.es6
@@ -0,0 +1,5 @@
+/* eslint-disable */
+Vue.filter('due-date', (value) => {
+ const date = new Date(value);
+ return $.datepicker.formatDate('M d, yy', date);
+});
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
index 44addb3ea98..5f99de39122 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
@@ -21,7 +22,7 @@
fallbackClass: 'is-dragging',
fallbackOnBody: true,
ghostClass: 'is-ghost',
- filter: '.has-tooltip',
+ filter: '.board-delete, .btn',
delay: gl.issueBoards.touchEnabled ? 100 : 0,
scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
scrollSpeed: 20,
diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6
index eb082103de9..21d735e8231 100644
--- a/app/assets/javascripts/boards/models/issue.js.es6
+++ b/app/assets/javascripts/boards/models/issue.js.es6
@@ -1,14 +1,21 @@
+/* eslint-disable */
class ListIssue {
constructor (obj) {
this.id = obj.iid;
this.title = obj.title;
this.confidential = obj.confidential;
+ this.dueDate = obj.due_date;
+ this.subscribed = obj.subscribed;
this.labels = [];
if (obj.assignee) {
this.assignee = new ListUser(obj.assignee);
}
+ if (obj.milestone) {
+ this.milestone = new ListMilestone(obj.milestone);
+ }
+
obj.labels.forEach((label) => {
this.labels.push(new ListLabel(label));
});
@@ -41,4 +48,21 @@ class ListIssue {
getLists () {
return gl.issueBoards.BoardsStore.state.lists.filter( list => list.findIssue(this.id) );
}
+
+ update (url) {
+ const data = {
+ issue: {
+ milestone_id: this.milestone ? this.milestone.id : null,
+ due_date: this.dueDate,
+ assignee_id: this.assignee ? this.assignee.id : null,
+ label_ids: this.labels.map( (label) => label.id )
+ }
+ };
+
+ if (!data.issue.label_ids.length) {
+ data.issue.label_ids = [''];
+ }
+
+ return Vue.http.patch(url, data);
+ }
}
diff --git a/app/assets/javascripts/boards/models/label.js.es6 b/app/assets/javascripts/boards/models/label.js.es6
index 583829552cd..0910fe9a854 100644
--- a/app/assets/javascripts/boards/models/label.js.es6
+++ b/app/assets/javascripts/boards/models/label.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class ListLabel {
constructor (obj) {
this.id = obj.id;
diff --git a/app/assets/javascripts/boards/models/list.js.es6 b/app/assets/javascripts/boards/models/list.js.es6
index 91fd620fdb3..8a7dc67409e 100644
--- a/app/assets/javascripts/boards/models/list.js.es6
+++ b/app/assets/javascripts/boards/models/list.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class List {
constructor (obj) {
this.id = obj.id;
@@ -41,7 +42,8 @@ class List {
}
destroy () {
- gl.issueBoards.BoardsStore.state.lists.$remove(this);
+ const index = gl.issueBoards.BoardsStore.state.lists.indexOf(this);
+ gl.issueBoards.BoardsStore.state.lists.splice(index, 1);
gl.issueBoards.BoardsStore.updateNewListDropdown(this.id);
gl.boardService.destroyList(this.id);
@@ -87,6 +89,17 @@ class List {
});
}
+ newIssue (issue) {
+ this.addIssue(issue);
+ this.issuesSize++;
+
+ return gl.boardService.newIssue(this.id, issue)
+ .then((resp) => {
+ const data = resp.json();
+ issue.id = data.iid;
+ });
+ }
+
createIssues (data) {
data.forEach((issueObj) => {
this.addIssue(new ListIssue(issueObj));
diff --git a/app/assets/javascripts/boards/models/milestone.js.es6 b/app/assets/javascripts/boards/models/milestone.js.es6
new file mode 100644
index 00000000000..a48969e19c9
--- /dev/null
+++ b/app/assets/javascripts/boards/models/milestone.js.es6
@@ -0,0 +1,7 @@
+/* eslint-disable */
+class ListMilestone {
+ constructor (obj) {
+ this.id = obj.id;
+ this.title = obj.title;
+ }
+}
diff --git a/app/assets/javascripts/boards/models/user.js.es6 b/app/assets/javascripts/boards/models/user.js.es6
index 904b3a68507..583a973fc46 100644
--- a/app/assets/javascripts/boards/models/user.js.es6
+++ b/app/assets/javascripts/boards/models/user.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class ListUser {
constructor (user) {
this.id = user.id;
diff --git a/app/assets/javascripts/boards/services/board_service.js.es6 b/app/assets/javascripts/boards/services/board_service.js.es6
index 9b80fb2e99f..f59a2ed7937 100644
--- a/app/assets/javascripts/boards/services/board_service.js.es6
+++ b/app/assets/javascripts/boards/services/board_service.js.es6
@@ -1,15 +1,14 @@
+/* eslint-disable */
class BoardService {
- constructor (root) {
- Vue.http.options.root = root;
-
- this.lists = Vue.resource(`${root}/lists{/id}`, {}, {
+ constructor (root, boardId) {
+ this.lists = Vue.resource(`${root}/${boardId}/lists{/id}`, {}, {
generate: {
method: 'POST',
- url: `${root}/lists/generate.json`
+ url: `${root}/${boardId}/lists/generate.json`
}
});
- this.issue = Vue.resource(`${root}/issues{/id}`, {});
- this.issues = Vue.resource(`${root}/lists{/id}/issues`, {});
+ this.issue = Vue.resource(`${root}/${boardId}/issues{/id}`, {});
+ this.issues = Vue.resource(`${root}/${boardId}/lists{/id}/issues`, {});
Vue.http.interceptors.push((request, next) => {
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
@@ -58,4 +57,10 @@ class BoardService {
to_list_id
});
}
+
+ newIssue (id, issue) {
+ return this.issues.save({ id }, {
+ issue
+ });
+ }
};
diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6
index bd07ee0c161..6bc95aa60f2 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js.es6
+++ b/app/assets/javascripts/boards/stores/boards_store.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
@@ -5,6 +6,9 @@
gl.issueBoards.BoardsStore = {
disabled: false,
state: {},
+ detail: {
+ issue: {}
+ },
moving: {
issue: {},
list: {}
@@ -35,6 +39,8 @@
// Remove any new issues from the backlog
// as they will be visible in the new list
list.issues.forEach(backlogList.removeIssue.bind(backlogList));
+
+ this.state.lists = _.sortBy(this.state.lists, 'position');
});
this.removeBlankState();
},
@@ -54,16 +60,19 @@
title: 'Welcome to your Issue Board!',
position: 0
});
+
+ this.state.lists = _.sortBy(this.state.lists, 'position');
},
removeBlankState () {
this.removeList('blank');
- $.cookie('issue_board_welcome_hidden', 'true', {
- expires: 365 * 10
+ Cookies.set('issue_board_welcome_hidden', 'true', {
+ expires: 365 * 10,
+ path: ''
});
},
welcomeIsHidden () {
- return $.cookie('issue_board_welcome_hidden') === 'true';
+ return Cookies.get('issue_board_welcome_hidden') === 'true';
},
removeList (id, type = 'blank') {
const list = this.findList('id', id, type);
diff --git a/app/assets/javascripts/boards/test_utils/simulate_drag.js b/app/assets/javascripts/boards/test_utils/simulate_drag.js
index 75f8b730195..01e09ec482e 100644
--- a/app/assets/javascripts/boards/test_utils/simulate_drag.js
+++ b/app/assets/javascripts/boards/test_utils/simulate_drag.js
@@ -1,3 +1,4 @@
+/* eslint-disable wrap-iife, func-names, strict, indent, no-tabs, no-var, vars-on-top, no-param-reassign, object-shorthand, no-shadow, comma-dangle, prefer-template, consistent-return, no-mixed-operators, no-unused-vars, object-curly-spacing, no-unused-expressions, prefer-arrow-callback, max-len */
(function () {
'use strict';
diff --git a/app/assets/javascripts/boards/vue_resource_interceptor.js.es6 b/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
index b5ff3a81ed5..80f137ca12e 100644
--- a/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
+++ b/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
Vue.http.interceptors.push((request, next) => {
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
diff --git a/app/assets/javascripts/breakpoints.js b/app/assets/javascripts/breakpoints.js
index 5fef9725178..e7ceb602601 100644
--- a/app/assets/javascripts/breakpoints.js
+++ b/app/assets/javascripts/breakpoints.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, quotes, no-shadow, prefer-arrow-callback, prefer-template, consistent-return, padded-blocks, no-return-assign, new-parens, no-param-reassign, no-undef, max-len */
(function() {
this.Breakpoints = (function() {
var BreakpointInstance, instance;
diff --git a/app/assets/javascripts/broadcast_message.js b/app/assets/javascripts/broadcast_message.js
index fceeff36728..30432dae278 100644
--- a/app/assets/javascripts/broadcast_message.js
+++ b/app/assets/javascripts/broadcast_message.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, no-else-return, object-shorthand, comma-dangle, padded-blocks, max-len */
(function() {
$(function() {
var previewPath;
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 10abeb50f4b..116a47b0907 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-param-reassign, no-undef, quotes, yoda, no-else-return, consistent-return, comma-dangle, semi, object-shorthand, prefer-template, one-var, one-var-declaration-per-line, no-unused-vars, max-len, vars-on-top, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -7,69 +8,86 @@
Build.state = null;
function Build(options) {
- this.page_url = options.page_url;
- this.build_url = options.build_url;
- this.build_status = options.build_status;
- this.state = options.state1;
- this.build_stage = options.build_stage;
- this.hideSidebar = bind(this.hideSidebar, this);
- this.toggleSidebar = bind(this.toggleSidebar, this);
+ options = options || $('.js-build-options').data();
+ this.pageUrl = options.pageUrl;
+ this.buildUrl = options.buildUrl;
+ this.buildStatus = options.buildStatus;
+ this.state = options.logState;
+ this.buildStage = options.buildStage;
this.updateDropdown = bind(this.updateDropdown, this);
+ this.$document = $(document);
clearInterval(Build.interval);
// Init breakpoint checker
this.bp = Breakpoints.get();
- $('.js-build-sidebar').niceScroll();
- this.populateJobs(this.build_stage);
- this.updateStageDropdownText(this.build_stage);
- this.hideSidebar();
+ this.initSidebar();
+ this.$buildScroll = $('#js-build-scroll');
- $(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
- $(window).off('resize.build').on('resize.build', this.hideSidebar);
- $(document).off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
+ 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);
+ $(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this));
+ $('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace);
this.updateArtifactRemoveDate();
if ($('#build-trace').length) {
this.getInitialBuildTrace();
this.initScrollButtonAffix();
}
- if (this.build_status === "running" || this.build_status === "pending") {
+ if (this.buildStatus === "running" || this.buildStatus === "pending") {
+ // Bind autoscroll button to follow build output
$('#autoscroll-button').on('click', function() {
var state;
state = $(this).data("state");
if ("enabled" === state) {
$(this).data("state", "disabled");
- return $(this).text("enable autoscroll");
+ return $(this).text("Enable autoscroll");
} else {
$(this).data("state", "enabled");
- return $(this).text("disable autoscroll");
+ return $(this).text("Disable autoscroll");
}
- //
- // Bind autoscroll button to follow build output
- //
});
Build.interval = setInterval((function(_this) {
+ // Check for new build output if user still watching build page
+ // Only valid for runnig build when output changes during time
return function() {
- if (window.location.href.split("#").first() === _this.page_url) {
+ if (_this.location() === _this.pageUrl) {
return _this.getBuildTrace();
}
};
- //
- // Check for new build output if user still watching build page
- // Only valid for runnig build when output changes during time
- //
})(this), 4000);
}
}
+ Build.prototype.initSidebar = function() {
+ this.$sidebar = $('.js-build-sidebar');
+ this.sidebarTranslationLimits = {
+ min: $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
+ }
+ this.sidebarTranslationLimits.max = this.sidebarTranslationLimits.min + $('.scrolling-tabs-container').outerHeight();
+ this.$sidebar.css({
+ top: this.sidebarTranslationLimits.max
+ });
+ this.$sidebar.niceScroll();
+ this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
+ this.$document.off('scroll.translateSidebar').on('scroll.translateSidebar', this.translateSidebar.bind(this));
+ };
+
+ Build.prototype.location = function() {
+ return window.location.href.split("#")[0];
+ };
+
Build.prototype.getInitialBuildTrace = function() {
var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']
return $.ajax({
- url: this.build_url,
+ url: this.buildUrl,
dataType: 'json',
- success: function(build_data) {
- $('.js-build-output').html(build_data.trace_html);
- if (removeRefreshStatuses.indexOf(build_data.status) >= 0) {
+ success: function(buildData) {
+ $('.js-build-output').html(buildData.trace_html);
+ if (removeRefreshStatuses.indexOf(buildData.status) >= 0) {
return $('.js-build-refresh').remove();
}
}
@@ -78,7 +96,7 @@
Build.prototype.getBuildTrace = function() {
return $.ajax({
- url: this.page_url + "/trace.json?state=" + (encodeURIComponent(this.state)),
+ url: this.pageUrl + "/trace.json?state=" + (encodeURIComponent(this.state)),
dataType: "json",
success: (function(_this) {
return function(log) {
@@ -92,8 +110,8 @@
$('.js-build-output').html(log.html);
}
return _this.checkAutoscroll();
- } else if (log.status !== _this.build_status) {
- return Turbolinks.visit(_this.page_url);
+ } else if (log.status !== _this.buildStatus) {
+ return Turbolinks.visit(_this.pageUrl);
}
};
})(this)
@@ -107,11 +125,10 @@
};
Build.prototype.initScrollButtonAffix = function() {
- var $body, $buildScroll, $buildTrace;
- $buildScroll = $('#js-build-scroll');
+ var $body, $buildTrace;
$body = $('body');
$buildTrace = $('#build-trace');
- return $buildScroll.affix({
+ return this.$buildScroll.affix({
offset: {
bottom: function() {
return $body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top);
@@ -120,24 +137,34 @@
});
};
- Build.prototype.shouldHideSidebar = function() {
+ Build.prototype.shouldHideSidebarForViewport = function() {
var bootstrapBreakpoint;
bootstrapBreakpoint = this.bp.getBreakpointSize();
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
};
- Build.prototype.toggleSidebar = function() {
- if (this.shouldHideSidebar()) {
- return $('.js-build-sidebar').toggleClass('right-sidebar-expanded right-sidebar-collapsed');
- }
+ Build.prototype.translateSidebar = function(e) {
+ var newPosition = this.sidebarTranslationLimits.max - (document.body.scrollTop || document.documentElement.scrollTop);
+ if (newPosition < this.sidebarTranslationLimits.min) newPosition = this.sidebarTranslationLimits.min;
+ this.$sidebar.css({
+ top: newPosition
+ });
};
- Build.prototype.hideSidebar = function() {
- if (this.shouldHideSidebar()) {
- return $('.js-build-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
- } else {
- return $('.js-build-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
- }
+ Build.prototype.toggleSidebar = function(shouldHide) {
+ var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
+ this.$buildScroll.toggleClass('sidebar-expanded', shouldShow)
+ .toggleClass('sidebar-collapsed', shouldHide);
+ this.$sidebar.toggleClass('right-sidebar-expanded', shouldShow)
+ .toggleClass('right-sidebar-collapsed', shouldHide);
+ };
+
+ Build.prototype.sidebarOnResize = function() {
+ this.toggleSidebar(this.shouldHideSidebarForViewport());
+ };
+
+ Build.prototype.sidebarOnClick = function() {
+ if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
};
Build.prototype.updateArtifactRemoveDate = function() {
@@ -145,7 +172,7 @@
$date = $('.js-artifacts-remove');
if ($date.length) {
date = $date.text();
- return $date.text($.timefor(new Date(date.replace(/-/g, '/')), ' '));
+ return $date.text(gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '));
}
};
@@ -165,6 +192,14 @@
this.populateJobs(stage);
};
+ Build.prototype.stepTrace = function(e) {
+ e.preventDefault();
+ $currentTarget = $(e.currentTarget);
+ $.scrollTo($currentTarget.attr('href'), {
+ offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
+ });
+ };
+
return Build;
})();
diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js
index f345ba0abe6..c423a548a30 100644
--- a/app/assets/javascripts/build_artifacts.js
+++ b/app/assets/javascripts/build_artifacts.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, padded-blocks, max-len */
(function() {
this.BuildArtifacts = (function() {
function BuildArtifacts() {
diff --git a/app/assets/javascripts/build_variables.js.es6 b/app/assets/javascripts/build_variables.js.es6
index 8d3e29794a1..0ecd20bc11e 100644
--- a/app/assets/javascripts/build_variables.js.es6
+++ b/app/assets/javascripts/build_variables.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
$(function(){
$('.reveal-variables').off('click').on('click',function(){
$('.js-build').toggle().niceScroll();
diff --git a/app/assets/javascripts/commit.js b/app/assets/javascripts/commit.js
index 23cf5b519f4..67509ea7d91 100644
--- a/app/assets/javascripts/commit.js
+++ b/app/assets/javascripts/commit.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-undef, padded-blocks */
(function() {
this.Commit = (function() {
function Commit() {
diff --git a/app/assets/javascripts/commit/file.js b/app/assets/javascripts/commit/file.js
index be24ee56aad..3f29826fa9b 100644
--- a/app/assets/javascripts/commit/file.js
+++ b/app/assets/javascripts/commit/file.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, no-undef, padded-blocks, max-len */
(function() {
this.CommitFile = (function() {
function CommitFile(file) {
diff --git a/app/assets/javascripts/commit/image-file.js b/app/assets/javascripts/commit/image_file.js
index e893491b19b..4c2ae595319 100644
--- a/app/assets/javascripts/commit/image-file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, 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, object-shorthand, padded-blocks, max-len */
(function() {
this.ImageFile = (function() {
var prepareFrames;
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 9132089adcd..951fb338f9d 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-undef, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, padded-blocks, max-len */
(function() {
this.CommitsList = (function() {
function CommitsList() {}
diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js
index 342ac0e8e69..d4243baadb5 100644
--- a/app/assets/javascripts/compare.js
+++ b/app/assets/javascripts/compare.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, padded-blocks, max-len */
(function() {
this.Compare = (function() {
function Compare(opts) {
@@ -79,7 +80,8 @@
success: function(html) {
loading.hide();
$target.html(html);
- return $('.js-timeago', $target).timeago();
+ var className = '.' + $target[0].className.replace(' ', '.');
+ gl.utils.localTimeAgo($('.js-timeago', className));
}
});
};
diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js.es6
index 4e3a28cd163..bd980f87e72 100644
--- a/app/assets/javascripts/compare_autocomplete.js
+++ b/app/assets/javascripts/compare_autocomplete.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
this.CompareAutocomplete = (function() {
function CompareAutocomplete() {
@@ -9,7 +10,10 @@
var $dropdown, selected;
$dropdown = $(this);
selected = $dropdown.data('selected');
- return $dropdown.glDropdown({
+ const $dropdownContainer = $dropdown.closest('.dropdown');
+ const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer);
+ const $filterInput = $('input[type="search"]', $dropdownContainer);
+ $dropdown.glDropdown({
data: function(term, callback) {
return $.ajax({
url: $dropdown.data('refs-url'),
@@ -23,8 +27,9 @@
selectable: true,
filterable: true,
filterByText: true,
- fieldName: $dropdown.attr('name'),
- filterInput: 'input[type="text"]',
+ toggleLabel: true,
+ fieldName: $dropdown.data('field-name'),
+ filterInput: 'input[type="search"]',
renderRow: function(ref) {
var link;
if (ref.header != null) {
@@ -41,6 +46,14 @@
return $el.text().trim();
}
});
+ $filterInput.on('keyup', (e) => {
+ const keyCode = e.keyCode || e.which;
+ if (keyCode !== 13) return;
+ const text = $filterInput.val();
+ $fieldInput.val(text);
+ $('.dropdown-toggle-text', $dropdown).text(text);
+ $dropdownContainer.removeClass('open');
+ });
});
};
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index 708ab08ffac..686a48486f3 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, camelcase, one-var-declaration-per-line, no-else-return, padded-blocks, max-len */
(function() {
this.ConfirmDangerModal = (function() {
function ConfirmDangerModal(form, text) {
@@ -11,7 +12,7 @@
submit.disable();
$('.js-confirm-danger-input').off('input');
$('.js-confirm-danger-input').on('input', function() {
- if (rstrip($(this).val()) === project_path) {
+ if (gl.utils.rstrip($(this).val()) === project_path) {
return submit.enable();
} else {
return submit.disable();
diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js
index 3e20db7e308..1cc34e490c2 100644
--- a/app/assets/javascripts/copy_to_clipboard.js
+++ b/app/assets/javascripts/copy_to_clipboard.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-undef, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, padded-blocks, max-len */
/*= require clipboard */
@@ -26,15 +27,15 @@
};
showTooltip = function(target, title) {
- return $(target).tooltip({
- container: 'body',
- html: 'true',
- placement: 'auto bottom',
- title: title,
- trigger: 'manual'
- }).tooltip('show').one('mouseleave', function() {
- return $(this).tooltip('hide');
- });
+ var $target = $(target);
+ var originalTitle = $target.data('original-title');
+
+ $target
+ .attr('title', 'Copied!')
+ .tooltip('fixTitle')
+ .tooltip('show')
+ .attr('title', originalTitle)
+ .tooltip('fixTitle');
};
$(function() {
diff --git a/app/assets/javascripts/create_label.js.es6 b/app/assets/javascripts/create_label.js.es6
index 46d1c3f00c1..744aa0afa03 100644
--- a/app/assets/javascripts/create_label.js.es6
+++ b/app/assets/javascripts/create_label.js.es6
@@ -1,8 +1,10 @@
+/* eslint-disable */
(function (w) {
class CreateLabelDropdown {
- constructor ($el, projectId) {
+ constructor ($el, namespacePath, projectPath) {
this.$el = $el;
- this.projectId = projectId;
+ this.namespacePath = namespacePath;
+ this.projectPath = projectPath;
this.$dropdownBack = $('.dropdown-menu-back', this.$el.closest('.dropdown'));
this.$cancelButton = $('.js-cancel-label-btn', this.$el);
this.$newLabelField = $('#new_label_name', this.$el);
@@ -91,8 +93,8 @@
e.preventDefault();
e.stopPropagation();
- Api.newLabel(this.projectId, {
- name: this.$newLabelField.val(),
+ Api.newLabel(this.namespacePath, this.projectPath, {
+ title: this.$newLabelField.val(),
color: this.$newColorField.val()
}, (label) => {
this.$newLabelCreateButton.enable();
@@ -113,6 +115,8 @@
.show();
} else {
this.$dropdownBack.trigger('click');
+
+ $(document).trigger('created.label', label);
}
});
}
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js.es6
new file mode 100644
index 00000000000..b83a4c63fad
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js.es6
@@ -0,0 +1,45 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ global.cycleAnalytics.StageCodeComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ </div>
+ <ul class="stage-event-list">
+ <li v-for="mergeRequest in items" class="stage-event-item">
+ <div class="item-details">
+ <img class="avatar" :src="mergeRequest.author.avatarUrl">
+ <h5 class="item-title merge-merquest-title">
+ <a :href="mergeRequest.url">
+ {{ mergeRequest.title }}
+ </a>
+ </h5>
+ <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
+ &middot;
+ <span>
+ Opened
+ <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
+ </span>
+ <span>
+ by
+ <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="mergeRequest.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+ });
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js.es6
new file mode 100644
index 00000000000..cb1687dcc7a
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js.es6
@@ -0,0 +1,47 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ global.cycleAnalytics.StageIssueComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ </div>
+ <ul class="stage-event-list">
+ <li v-for="issue in items" class="stage-event-item">
+ <div class="item-details">
+ <img class="avatar" :src="issue.author.avatarUrl">
+ <h5 class="item-title issue-title">
+ <a class="issue-title" :href="issue.url">
+ {{ issue.title }}
+ </a>
+ </h5>
+ <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
+ &middot;
+ <span>
+ Opened
+ <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
+ </span>
+ <span>
+ by
+ <a :href="issue.author.webUrl" class="issue-author-link">
+ {{ issue.author.name }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="issue.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+ });
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js.es6
new file mode 100644
index 00000000000..513298ba4e7
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js.es6
@@ -0,0 +1,44 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ global.cycleAnalytics.StagePlanComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ </div>
+ <ul class="stage-event-list">
+ <li v-for="commit in items" class="stage-event-item">
+ <div class="item-details item-conmmit-component">
+ <img class="avatar" :src="commit.author.avatarUrl">
+ <h5 class="item-title commit-title">
+ <a :href="commit.commitUrl">
+ {{ commit.title }}
+ </a>
+ </h5>
+ <span>
+ First
+ <span class="commit-icon">${global.cycleAnalytics.svgs.iconCommit}</span>
+ <a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
+ pushed by
+ <a :href="commit.author.webUrl" class="commit-author-link">
+ {{ commit.author.name }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="commit.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+ });
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js.es6
new file mode 100644
index 00000000000..73f4205b578
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js.es6
@@ -0,0 +1,47 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ global.cycleAnalytics.StageProductionComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ </div>
+ <ul class="stage-event-list">
+ <li v-for="issue in items" class="stage-event-item">
+ <div class="item-details">
+ <img class="avatar" :src="issue.author.avatarUrl">
+ <h5 class="item-title issue-title">
+ <a class="issue-title" :href="issue.url">
+ {{ issue.title }}
+ </a>
+ </h5>
+ <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
+ &middot;
+ <span>
+ Opened
+ <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
+ </span>
+ <span>
+ by
+ <a :href="issue.author.webUrl" class="issue-author-link">
+ {{ issue.author.name }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="issue.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+ });
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js.es6
new file mode 100644
index 00000000000..501ffb1fac9
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js.es6
@@ -0,0 +1,57 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ global.cycleAnalytics.StageReviewComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ </div>
+ <ul class="stage-event-list">
+ <li v-for="mergeRequest in items" class="stage-event-item">
+ <div class="item-details">
+ <img class="avatar" :src="mergeRequest.author.avatarUrl">
+ <h5 class="item-title merge-merquest-title">
+ <a :href="mergeRequest.url">
+ {{ mergeRequest.title }}
+ </a>
+ </h5>
+ <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
+ &middot;
+ <span>
+ Opened
+ <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
+ </span>
+ <span>
+ by
+ <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
+ </span>
+ <template v-if="mergeRequest.state === 'closed'">
+ <span class="merge-request-state">
+ <i class="fa fa-ban"></i>
+ {{ mergeRequest.state.toUpperCase() }}
+ </span>
+ </template>
+ <template v-else>
+ <span class="merge-request-branch" v-if="mergeRequest.branch">
+ <i class= "fa fa-code-fork"></i>
+ <a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a>
+ </span>
+ </template>
+ </div>
+ <div class="item-time">
+ <total-time :time="mergeRequest.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+ });
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js.es6
new file mode 100644
index 00000000000..82622232f64
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js.es6
@@ -0,0 +1,44 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ global.cycleAnalytics.StageStagingComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ </div>
+ <ul class="stage-event-list">
+ <li v-for="build in items" class="stage-event-item item-build-component">
+ <div class="item-details">
+ <img class="avatar" :src="build.author.avatarUrl">
+ <h5 class="item-title">
+ <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
+ <i class="fa fa-code-fork"></i>
+ <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
+ <span class="icon-branch">${global.cycleAnalytics.svgs.iconBranch}</span>
+ <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
+ </h5>
+ <span>
+ <a :href="build.url" class="build-date">{{ build.date }}</a>
+ by
+ <a :href="build.author.webUrl" class="issue-author-link">
+ {{ build.author.name }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="build.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+ });
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js.es6
new file mode 100644
index 00000000000..4bfd363a1f1
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js.es6
@@ -0,0 +1,44 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ global.cycleAnalytics.StageTestComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ </div>
+ <ul class="stage-event-list">
+ <li v-for="build in items" class="stage-event-item item-build-component">
+ <div class="item-details">
+ <h5 class="item-title">
+ <span class="icon-build-status">${global.cycleAnalytics.svgs.iconBuildStatus}</span>
+ <a :href="build.url" class="item-build-name">{{ build.name }}</a>
+ &middot;
+ <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
+ <i class="fa fa-code-fork"></i>
+ <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
+ <span class="icon-branch">${global.cycleAnalytics.svgs.iconBranch}</span>
+ <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
+ </h5>
+ <span>
+ <a :href="build.url" class="issue-date">
+ {{ build.date }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="build.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+ });
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6
new file mode 100644
index 00000000000..b9675f50e31
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6
@@ -0,0 +1,20 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ global.cycleAnalytics.TotalTimeComponent = Vue.extend({
+ props: {
+ time: Object,
+ },
+ template: `
+ <span class="total-time">
+ <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
+ <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
+ <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
+ <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
+ </span>
+ `,
+ });
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
new file mode 100644
index 00000000000..2f810a69758
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
@@ -0,0 +1,125 @@
+/* global Vue */
+/* global Cookies */
+/* global Flash */
+
+//= require vue
+//= require_tree ./svg
+//= require_tree .
+
+$(() => {
+ const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
+ const cycleAnalyticsEl = document.querySelector('#cycle-analytics');
+ const cycleAnalyticsStore = gl.cycleAnalytics.CycleAnalyticsStore;
+ const cycleAnalyticsService = new gl.cycleAnalytics.CycleAnalyticsService({
+ requestPath: cycleAnalyticsEl.dataset.requestPath,
+ });
+
+ gl.cycleAnalyticsApp = new Vue({
+ el: '#cycle-analytics',
+ name: 'CycleAnalytics',
+ data: {
+ state: cycleAnalyticsStore.state,
+ isLoading: false,
+ isLoadingStage: false,
+ isEmptyStage: false,
+ hasError: false,
+ startDate: 30,
+ isOverviewDialogDismissed: Cookies.get(OVERVIEW_DIALOG_COOKIE),
+ },
+ computed: {
+ currentStage() {
+ return cycleAnalyticsStore.currentActiveStage();
+ },
+ },
+ components: {
+ 'stage-issue-component': gl.cycleAnalytics.StageIssueComponent,
+ 'stage-plan-component': gl.cycleAnalytics.StagePlanComponent,
+ 'stage-code-component': gl.cycleAnalytics.StageCodeComponent,
+ 'stage-test-component': gl.cycleAnalytics.StageTestComponent,
+ 'stage-review-component': gl.cycleAnalytics.StageReviewComponent,
+ 'stage-staging-component': gl.cycleAnalytics.StageStagingComponent,
+ 'stage-production-component': gl.cycleAnalytics.StageProductionComponent,
+ },
+ created() {
+ this.fetchCycleAnalyticsData();
+ },
+ methods: {
+ handleError() {
+ cycleAnalyticsStore.setErrorState(true);
+ return new Flash('There was an error while fetching cycle analytics data.');
+ },
+ initDropdown() {
+ 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');
+
+ $label.text($target.text().trim());
+ this.fetchCycleAnalyticsData({ startDate: this.startDate });
+ });
+ },
+ fetchCycleAnalyticsData(options) {
+ const fetchOptions = options || { startDate: this.startDate };
+
+ this.isLoading = true;
+
+ cycleAnalyticsService
+ .fetchCycleAnalyticsData(fetchOptions)
+ .done((response) => {
+ cycleAnalyticsStore.setCycleAnalyticsData(response);
+ this.selectDefaultStage();
+ this.initDropdown();
+ })
+ .error(() => {
+ this.handleError();
+ })
+ .always(() => {
+ this.isLoading = false;
+ });
+ },
+ selectDefaultStage() {
+ const stage = this.state.stages.first();
+ this.selectStage(stage);
+ },
+ selectStage(stage) {
+ if (this.isLoadingStage) return;
+ if (this.currentStage === stage) return;
+
+ if (!stage.isUserAllowed) {
+ cycleAnalyticsStore.setActiveStage(stage);
+ return;
+ }
+
+ this.isLoadingStage = true;
+ cycleAnalyticsStore.setStageEvents([]);
+ cycleAnalyticsStore.setActiveStage(stage);
+
+ cycleAnalyticsService
+ .fetchStageData({
+ stage,
+ startDate: this.startDate,
+ })
+ .done((response) => {
+ this.isEmptyStage = !response.events.length;
+ cycleAnalyticsStore.setStageEvents(response.events);
+ })
+ .error(() => {
+ this.isEmptyStage = true;
+ })
+ .always(() => {
+ this.isLoadingStage = false;
+ });
+ },
+ dismissOverviewDialog() {
+ this.isOverviewDialogDismissed = true;
+ Cookies.set(OVERVIEW_DIALOG_COOKIE, '1');
+ },
+ },
+ });
+
+ // Register global components
+ Vue.component('total-time', gl.cycleAnalytics.TotalTimeComponent);
+});
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js.es6
new file mode 100644
index 00000000000..9f74b14c4b9
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js.es6
@@ -0,0 +1,41 @@
+/* eslint-disable no-param-reassign */
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ class CycleAnalyticsService {
+ constructor(options) {
+ this.requestPath = options.requestPath;
+ }
+
+ fetchCycleAnalyticsData(options) {
+ options = options || { startDate: 30 };
+
+ return $.ajax({
+ url: this.requestPath,
+ method: 'GET',
+ dataType: 'json',
+ contentType: 'application/json',
+ data: {
+ cycle_analytics: {
+ start_date: options.startDate,
+ },
+ },
+ });
+ }
+
+ fetchStageData(options) {
+ const {
+ stage,
+ startDate,
+ } = options;
+
+ return $.get(`${this.requestPath}/events/${stage.title.toLowerCase()}.json`, {
+ cycle_analytics: {
+ start_date: startDate,
+ },
+ });
+ }
+ }
+
+ global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6
new file mode 100644
index 00000000000..9b905874167
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js.es6
@@ -0,0 +1,90 @@
+/* eslint-disable no-param-reassign */
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+
+ 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.',
+ };
+
+ global.cycleAnalytics.CycleAnalyticsStore = {
+ state: {
+ summary: '',
+ stats: '',
+ analytics: '',
+ events: [],
+ stages: [],
+ },
+ setCycleAnalyticsData(data) {
+ this.state = Object.assign(this.state, this.decorateData(data));
+ },
+ decorateData(data) {
+ const newData = {};
+
+ newData.stages = data.stats || [];
+ newData.summary = data.summary || [];
+
+ newData.summary.forEach((item) => {
+ item.value = item.value || '-';
+ });
+
+ newData.stages.forEach((item) => {
+ const stageName = item.title.toLowerCase();
+ item.active = false;
+ item.isUserAllowed = data.permissions[stageName];
+ item.emptyStageText = EMPTY_STAGE_TEXTS[stageName];
+ item.component = `stage-${stageName}-component`;
+ });
+ newData.analytics = data;
+ return newData;
+ },
+ setLoadingState(state) {
+ this.state.isLoading = state;
+ },
+ setErrorState(state) {
+ this.state.hasError = state;
+ },
+ deactivateAllStages() {
+ this.state.stages.forEach((stage) => {
+ stage.active = false;
+ });
+ },
+ setActiveStage(stage) {
+ this.deactivateAllStages();
+ stage.active = true;
+ },
+ setStageEvents(events) {
+ this.state.events = this.decorateEvents(events);
+ },
+ decorateEvents(events) {
+ const newEvents = events;
+
+ newEvents.forEach((item) => {
+ item.totalTime = item.total_time;
+ item.author.webUrl = item.author.web_url;
+ item.author.avatarUrl = item.author.avatar_url;
+
+ if (item.created_at) item.createdAt = item.created_at;
+ if (item.short_sha) item.shortSha = item.short_sha;
+ if (item.commit_url) item.commitUrl = item.commit_url;
+
+ delete item.author.web_url;
+ delete item.author.avatar_url;
+ delete item.total_time;
+ delete item.created_at;
+ delete item.short_sha;
+ delete item.commit_url;
+ });
+
+ return newEvents;
+ },
+ currentActiveStage() {
+ return this.state.stages.find(stage => stage.active);
+ },
+ };
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/svg/icon_branch.js.es6 b/app/assets/javascripts/cycle_analytics/svg/icon_branch.js.es6
new file mode 100644
index 00000000000..5d486bcaf66
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/svg/icon_branch.js.es6
@@ -0,0 +1,7 @@
+/* eslint-disable no-param-reassign */
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+ global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
+
+ global.cycleAnalytics.svgs.iconBranch = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><path fill="#8C8C8C" fill-rule="evenodd" d="M9.678 6.722C9.353 5.167 8.053 4 6.5 4S3.647 5.167 3.322 6.722h-2.6c-.397 0-.722.35-.722.778 0 .428.325.778.722.778h2.6C3.647 9.833 4.947 11 6.5 11s2.853-1.167 3.178-2.722h2.6c.397 0 .722-.35.722-.778 0-.428-.325-.778-.722-.778h-2.6zM4.694 7.5c0-1.09.795-1.944 1.806-1.944 1.01 0 1.806.855 1.806 1.944 0 1.09-.795 1.944-1.806 1.944-1.01 0-1.806-.855-1.806-1.944z"/></svg>';
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/svg/icon_build_status.js.es6 b/app/assets/javascripts/cycle_analytics/svg/icon_build_status.js.es6
new file mode 100644
index 00000000000..661bf9e9f1c
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/svg/icon_build_status.js.es6
@@ -0,0 +1,7 @@
+/* eslint-disable no-param-reassign */
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+ global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
+
+ global.cycleAnalytics.svgs.iconBuildStatus = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><g fill="#31AF64" fill-rule="evenodd"><path d="M12.5 7c0-3.038-2.462-5.5-5.5-5.5S1.5 3.962 1.5 7s2.462 5.5 5.5 5.5 5.5-2.462 5.5-5.5zM0 7c0-3.866 3.134-7 7-7s7 3.134 7 7-3.134 7-7 7-7-3.134-7-7z"/><path d="M6.28 7.697L5.045 6.464c-.117-.117-.305-.117-.42-.002l-.614.614c-.11.113-.11.303.007.42l1.91 1.91c.19.19.51.197.703.004l.264-.265L9.997 6.04c.108-.107.107-.293-.01-.408l-.612-.614c-.114-.113-.298-.12-.41-.01L6.28 7.7z"/></g></svg>';
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/cycle_analytics/svg/icon_commit.js.es6 b/app/assets/javascripts/cycle_analytics/svg/icon_commit.js.es6
new file mode 100644
index 00000000000..2208c27a619
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/svg/icon_commit.js.es6
@@ -0,0 +1,7 @@
+/* eslint-disable no-param-reassign */
+((global) => {
+ global.cycleAnalytics = global.cycleAnalytics || {};
+ global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
+
+ global.cycleAnalytics.svgs.iconCommit = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><path fill="#8F8F8F" fill-rule="evenodd" d="M28.777 18c-.91-4.008-4.494-7-8.777-7-4.283 0-7.868 2.992-8.777 7H4.01C2.9 18 2 18.895 2 20c0 1.112.9 2 2.01 2h7.213c.91 4.008 4.494 7 8.777 7 4.283 0 7.868-2.992 8.777-7h7.214C37.1 22 38 21.105 38 20c0-1.112-.9-2-2.01-2h-7.213zM20 25c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5z"/></svg>';
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index c8634b78f2b..00da5f17f9f 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, max-len, one-var, camelcase, one-var-declaration-per-line, no-unused-vars, no-unused-expressions, no-sequences, object-shorthand, comma-dangle, prefer-arrow-callback, semi, radix, padded-blocks, max-len */
(function() {
this.Diff = (function() {
var UNFOLD_COUNT;
@@ -7,6 +8,9 @@
function Diff() {
$('.files .diff-file').singleFileDiff();
this.filesCommentButton = $('.files .diff-file').filesCommentButton();
+ if (this.diffViewType() === 'parallel') {
+ $('.content-wrapper .container-fluid').removeClass('container-limited');
+ }
$(document).off('click', '.js-unfold');
$(document).on('click', '.js-unfold', (function(_this) {
return function(event) {
@@ -39,10 +43,6 @@
bottom: unfoldBottom,
offset: offset,
unfold: unfold,
- // indent is used to compensate for single space indent to fit
- // '+' and '-' prepended to diff lines,
- // see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
- indent: 1,
view: file.data('view')
};
return $.get(link, params, function(response) {
@@ -52,6 +52,10 @@
})(this));
}
+ Diff.prototype.diffViewType = function() {
+ return $('.inline-parallel-buttons a.active').data('view-type');
+ }
+
Diff.prototype.lineNumbers = function(line) {
if (!line.children().length) {
return [0, 0];
diff --git a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6 b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6
index 48bc7d77805..52e2846d279 100644
--- a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6
+++ b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es6
@@ -1,8 +1,13 @@
-((w) => {
- w.CommentAndResolveBtn = Vue.extend({
+/* eslint-disable */
+(() => {
+ const CommentAndResolveBtn = Vue.extend({
props: {
discussionId: String,
- textareaIsEmpty: Boolean
+ },
+ data() {
+ return {
+ textareaIsEmpty: true
+ }
},
computed: {
discussion: function () {
@@ -34,7 +39,7 @@
}
}
},
- ready: function () {
+ mounted: function () {
const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`);
this.textareaIsEmpty = $textarea.val() === '';
@@ -46,4 +51,6 @@
$(`#new-discussion-note-form-${this.discussionId} .note-textarea`).off('input.comment-and-resolve-btn');
}
});
+
+ Vue.component('comment-and-resolve-btn', CommentAndResolveBtn);
})(window);
diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6 b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6
index ad80d1118df..983e554b9c1 100644
--- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(() => {
JumpToDiscussion = Vue.extend({
mixins: [DiscussionMixins],
diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
index be6ebc77947..27af9fc96ad 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
@@ -1,13 +1,10 @@
-((w) => {
- w.ResolveBtn = Vue.extend({
- mixins: [
- ButtonMixins
- ],
+/* eslint-disable */
+(() => {
+ const ResolveBtn = Vue.extend({
props: {
noteId: Number,
discussionId: String,
resolved: Boolean,
- namespacePath: String,
projectPath: String,
canResolve: Boolean,
resolvedBy: String
@@ -57,7 +54,7 @@
},
methods: {
updateTooltip: function () {
- $(this.$els.button)
+ $(this.$refs.button)
.tooltip('hide')
.tooltip('fixTitle');
},
@@ -69,10 +66,10 @@
if (this.isResolved) {
promise = ResolveService
- .unresolve(this.namespace, this.noteId);
+ .unresolve(this.projectPath, this.noteId);
} else {
promise = ResolveService
- .resolve(this.namespace, this.noteId);
+ .resolve(this.projectPath, this.noteId);
}
promise.then((response) => {
@@ -92,8 +89,8 @@
});
}
},
- compiled: function () {
- $(this.$els.button).tooltip({
+ mounted: function () {
+ $(this.$refs.button).tooltip({
container: 'body'
});
},
@@ -104,4 +101,6 @@
CommentsStore.create(this.discussionId, this.noteId, this.canResolve, this.resolved, this.resolvedBy);
}
});
-})(window);
+
+ Vue.component('resolve-btn', ResolveBtn);
+})();
diff --git a/app/assets/javascripts/diff_notes/components/resolve_count.js.es6 b/app/assets/javascripts/diff_notes/components/resolve_count.js.es6
index 9e383b14a3e..9522ccb49da 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_count.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_count.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.ResolveCount = Vue.extend({
mixins: [DiscussionMixins],
@@ -12,6 +13,9 @@
computed: {
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.es6 b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6
index e373b06b1eb..b945a09fcbe 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es6
@@ -1,12 +1,9 @@
-((w) => {
- w.ResolveDiscussionBtn = Vue.extend({
- mixins: [
- ButtonMixins
- ],
+/* eslint-disable */
+(() => {
+ const ResolveDiscussionBtn = Vue.extend({
props: {
discussionId: String,
mergeRequestId: Number,
- namespacePath: String,
projectPath: String,
canResolve: Boolean,
},
@@ -50,11 +47,13 @@
},
methods: {
resolve: function () {
- ResolveService.toggleResolveForDiscussion(this.namespace, this.mergeRequestId, this.discussionId);
+ ResolveService.toggleResolveForDiscussion(this.projectPath, this.mergeRequestId, this.discussionId);
}
},
created: function () {
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
}
});
-})(window);
+
+ Vue.component('resolve-discussion-btn', ResolveDiscussionBtn);
+})();
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
index 22d9cf6c857..bd4c20aed8b 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
//= require vue
//= require vue-resource
//= require_directory ./models
@@ -7,24 +8,35 @@
//= require_directory ./components
$(() => {
- window.DiffNotesApp = new Vue({
- el: '#diff-notes-app',
- components: {
- 'resolve-btn': ResolveBtn,
- 'resolve-discussion-btn': ResolveDiscussionBtn,
- 'comment-and-resolve-btn': CommentAndResolveBtn
- },
- methods: {
- compileComponents: function () {
- const $components = $('resolve-btn, resolve-discussion-btn, jump-to-discussion');
- if ($components.length) {
- $components.each(function () {
- DiffNotesApp.$compile($(this).get(0));
- });
+ const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn';
+
+ window.gl = window.gl || {};
+ window.gl.diffNoteApps = {};
+
+ gl.diffNotesCompileComponents = () => {
+ const $components = $(COMPONENT_SELECTOR).filter(function () {
+ return $(this).closest('resolve-count').length !== 1;
+ });
+
+ if ($components) {
+ $components.each(function () {
+ const $this = $(this);
+ const noteId = $this.attr(':note-id');
+ const tmp = Vue.extend({
+ template: $this.get(0).outerHTML
+ });
+ const tmpApp = new tmp().$mount();
+
+ if (noteId) {
+ gl.diffNoteApps[`note_${noteId}`] = tmpApp;
}
- }
+
+ $this.replaceWith(tmpApp.$el);
+ });
}
- });
+ };
+
+ gl.diffNotesCompileComponents();
new Vue({
el: '#resolve-count-app',
diff --git a/app/assets/javascripts/diff_notes/mixins/discussion.js.es6 b/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
index a05f885201d..7a929017f36 100644
--- a/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.DiscussionMixins = {
computed: {
diff --git a/app/assets/javascripts/diff_notes/mixins/namespace.js.es6 b/app/assets/javascripts/diff_notes/mixins/namespace.js.es6
deleted file mode 100644
index d278678085b..00000000000
--- a/app/assets/javascripts/diff_notes/mixins/namespace.js.es6
+++ /dev/null
@@ -1,9 +0,0 @@
-((w) => {
- w.ButtonMixins = {
- computed: {
- namespace: function () {
- return `${this.namespacePath}/${this.projectPath}`;
- }
- }
- };
-})(window);
diff --git a/app/assets/javascripts/diff_notes/models/discussion.js.es6 b/app/assets/javascripts/diff_notes/models/discussion.js.es6
index 488714e4870..439f55520ef 100644
--- a/app/assets/javascripts/diff_notes/models/discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/models/discussion.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class DiscussionModel {
constructor (discussionId) {
this.id = discussionId;
diff --git a/app/assets/javascripts/diff_notes/models/note.js.es6 b/app/assets/javascripts/diff_notes/models/note.js.es6
index f2d2d389c38..d0541b02632 100644
--- a/app/assets/javascripts/diff_notes/models/note.js.es6
+++ b/app/assets/javascripts/diff_notes/models/note.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class NoteModel {
constructor (discussionId, noteId, canResolve, resolved, resolved_by) {
this.discussionId = discussionId;
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js.es6 b/app/assets/javascripts/diff_notes/services/resolve.js.es6
index de771ff814b..86953ce7ffb 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js.es6
+++ b/app/assets/javascripts/diff_notes/services/resolve.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
class ResolveServiceClass {
constructor() {
@@ -9,32 +10,32 @@
Vue.http.headers.common['X-CSRF-Token'] = $.rails.csrfToken();
}
- prepareRequest(namespace) {
+ prepareRequest(root) {
this.setCSRF();
- Vue.http.options.root = `/${namespace}`;
+ Vue.http.options.root = root;
}
- resolve(namespace, noteId) {
- this.prepareRequest(namespace);
+ resolve(projectPath, noteId) {
+ this.prepareRequest(projectPath);
return this.noteResource.save({ noteId }, {});
}
- unresolve(namespace, noteId) {
- this.prepareRequest(namespace);
+ unresolve(projectPath, noteId) {
+ this.prepareRequest(projectPath);
return this.noteResource.delete({ noteId }, {});
}
- toggleResolveForDiscussion(namespace, mergeRequestId, discussionId) {
+ toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId],
isResolved = discussion.isResolved();
let promise;
if (isResolved) {
- promise = this.unResolveAll(namespace, mergeRequestId, discussionId);
+ promise = this.unResolveAll(projectPath, mergeRequestId, discussionId);
} else {
- promise = this.resolveAll(namespace, mergeRequestId, discussionId);
+ promise = this.resolveAll(projectPath, mergeRequestId, discussionId);
}
promise.then((response) => {
@@ -57,10 +58,10 @@
})
}
- resolveAll(namespace, mergeRequestId, discussionId) {
+ resolveAll(projectPath, mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId];
- this.prepareRequest(namespace);
+ this.prepareRequest(projectPath);
discussion.loading = true;
@@ -70,10 +71,10 @@
}, {});
}
- unResolveAll(namespace, mergeRequestId, discussionId) {
+ unResolveAll(projectPath, mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId];
- this.prepareRequest(namespace);
+ this.prepareRequest(projectPath);
discussion.loading = true;
diff --git a/app/assets/javascripts/diff_notes/stores/comments.js.es6 b/app/assets/javascripts/diff_notes/stores/comments.js.es6
index 69522e1dac5..f42ca406bb1 100644
--- a/app/assets/javascripts/diff_notes/stores/comments.js.es6
+++ b/app/assets/javascripts/diff_notes/stores/comments.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((w) => {
w.CommentsStore = {
state: {},
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js.es6
index 99b16f7d59b..c2d4670b7e9 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var Dispatcher;
@@ -8,6 +9,7 @@
Dispatcher = (function() {
function Dispatcher() {
this.initSearch();
+ this.initFieldErrors();
this.initPageScripts();
}
@@ -20,13 +22,20 @@
path = page.split(':');
shortcut_handler = null;
switch (page) {
+ case 'sessions:new':
+ new UsernameValidator();
+ break;
case 'projects:boards:show':
+ case 'projects:boards:index':
shortcut_handler = new ShortcutsNavigation();
break;
+ case 'projects:builds:show':
+ new Build();
+ break;
case 'projects:merge_requests:index':
case 'projects:issues:index':
Issuable.init();
- new IssuableBulkActions();
+ new gl.IssuableBulkActions();
shortcut_handler = new ShortcutsNavigation();
break;
case 'projects:issues:show':
@@ -40,12 +49,12 @@
new Milestone();
break;
case 'dashboard:todos:index':
- new Todos();
+ new gl.Todos();
break;
case 'projects:milestones:new':
case 'projects:milestones:edit':
new ZenMode();
- new DueDateSelect();
+ new gl.DueDateSelectors();
new GLForm($('.milestone-form'));
break;
case 'groups:milestones:new':
@@ -59,7 +68,9 @@
shortcut_handler = new ShortcutsNavigation();
new GLForm($('.issue-form'));
new IssuableForm($('.issue-form'));
- new IssuableTemplateSelectors();
+ new LabelsSelect();
+ new MilestoneSelect();
+ new gl.IssuableTemplateSelectors();
break;
case 'projects:merge_requests:new':
case 'projects:merge_requests:edit':
@@ -67,7 +78,9 @@
shortcut_handler = new ShortcutsNavigation();
new GLForm($('.merge-request-form'));
new IssuableForm($('.merge-request-form'));
- new IssuableTemplateSelectors();
+ new LabelsSelect();
+ new MilestoneSelect();
+ new gl.IssuableTemplateSelectors();
break;
case 'projects:tags:new':
new ZenMode();
@@ -92,13 +105,15 @@
new ZenMode();
new MergedButtons();
break;
- case "projects:merge_requests:conflicts":
- window.mcui = new MergeConflictResolver()
+ case 'projects:merge_requests:index':
+ shortcut_handler = new ShortcutsNavigation();
+ Issuable.init();
+ break;
case 'dashboard:activity':
- new Activities();
+ new gl.Activities();
break;
case 'dashboard:projects:starred':
- new Activities();
+ new gl.Activities();
break;
case 'projects:commit:show':
new Commit();
@@ -106,6 +121,9 @@
new ZenMode();
shortcut_handler = new ShortcutsNavigation();
break;
+ case 'projects:commit:builds':
+ new gl.Pipelines();
+ break;
case 'projects:commits:show':
case 'projects:activity':
shortcut_handler = new ShortcutsNavigation();
@@ -117,8 +135,11 @@
new TreeView();
}
break;
+ case 'projects:pipelines:show':
+ new gl.Pipelines();
+ break;
case 'groups:activity':
- new Activities();
+ new gl.Activities();
break;
case 'groups:show':
shortcut_handler = new ShortcutsNavigation();
@@ -127,12 +148,12 @@
break;
case 'groups:group_members:index':
new gl.MemberExpirationDate();
- new GroupMembers();
+ new gl.Members();
new UsersSelect();
break;
case 'projects:project_members:index':
new gl.MemberExpirationDate();
- new ProjectMembers();
+ new gl.Members();
new UsersSelect();
break;
case 'groups:new':
@@ -154,13 +175,15 @@
shortcut_handler = new ShortcutsNavigation();
new ShortcutsBlob(true);
break;
+ case 'groups:labels:new':
+ case 'groups:labels:edit':
case 'projects:labels:new':
case 'projects:labels:edit':
new Labels();
break;
case 'projects:labels:index':
if ($('.prioritized-labels').length) {
- new LabelManager();
+ new gl.LabelManager();
}
break;
case 'projects:network:show':
@@ -271,10 +294,16 @@
Dispatcher.prototype.initSearch = function() {
// Only when search form is present
if ($('.search').length) {
- return new SearchAutocomplete();
+ return new gl.SearchAutocomplete();
}
};
+ Dispatcher.prototype.initFieldErrors = function() {
+ $('.gl-show-field-errors').each((i, form) => {
+ new gl.GlFieldErrors(form);
+ });
+ };
+
return Dispatcher;
})();
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 4a6fea929c7..e1e76bca6ad 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, no-undef, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, no-plusplus, prefer-arrow-callback, padded-blocks, max-len */
/*= require preview_markdown */
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
deleted file mode 100644
index bf68b7e3a9b..00000000000
--- a/app/assets/javascripts/due_date_select.js
+++ /dev/null
@@ -1,107 +0,0 @@
-(function() {
- this.DueDateSelect = (function() {
- function DueDateSelect() {
- var $datePicker, $dueDate, $loading;
- // Milestone edit/new form
- $datePicker = $('.datepicker');
- if ($datePicker.length) {
- $dueDate = $('#milestone_due_date');
- $datePicker.datepicker({
- dateFormat: 'yy-mm-dd',
- onSelect: function(dateText, inst) {
- return $dueDate.val(dateText);
- }
- }).datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $dueDate.val()));
- }
- $('.js-clear-due-date').on('click', function(e) {
- e.preventDefault();
- return $.datepicker._clearDate($datePicker);
- });
- // Issuable sidebar
- $loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
- $('.js-due-date-select').each(function(i, dropdown) {
- var $block, $dropdown, $dropdownParent, $selectbox, $sidebarValue, $value, $valueContent, abilityName, addDueDate, fieldName, issueUpdateURL;
- $dropdown = $(dropdown);
- $dropdownParent = $dropdown.closest('.dropdown');
- $datePicker = $dropdownParent.find('.js-due-date-calendar');
- $block = $dropdown.closest('.block');
- $selectbox = $dropdown.closest('.selectbox');
- $value = $block.find('.value');
- $valueContent = $block.find('.value-content');
- $sidebarValue = $('.js-due-date-sidebar-value', $block);
- fieldName = $dropdown.data('field-name');
- abilityName = $dropdown.data('ability-name');
- issueUpdateURL = $dropdown.data('issue-update');
- $dropdown.glDropdown({
- hidden: function() {
- $selectbox.hide();
- return $value.css('display', '');
- }
- });
- addDueDate = function(isDropdown) {
- var data, date, mediumDate, value;
- // Create the post date
- value = $("input[name='" + fieldName + "']").val();
- if (value !== '') {
- date = new Date(value.replace(new RegExp('-', 'g'), ','));
- mediumDate = $.datepicker.formatDate('M d, yy', date);
- } else {
- mediumDate = 'No due date';
- }
- data = {};
- data[abilityName] = {};
- data[abilityName].due_date = value;
- return $.ajax({
- type: 'PUT',
- url: issueUpdateURL,
- data: data,
- dataType: 'json',
- beforeSend: function() {
- var cssClass;
- $loading.fadeIn();
- if (isDropdown) {
- $dropdown.trigger('loading.gl.dropdown');
- $selectbox.hide();
- }
- $value.css('display', '');
- cssClass = Date.parse(mediumDate) ? 'bold' : 'no-value';
- $valueContent.html("<span class='" + cssClass + "'>" + mediumDate + "</span>");
- $sidebarValue.html(mediumDate);
- if (value !== '') {
- return $('.js-remove-due-date-holder').removeClass('hidden');
- } else {
- return $('.js-remove-due-date-holder').addClass('hidden');
- }
- }
- }).done(function(data) {
- if (isDropdown) {
- $dropdown.trigger('loaded.gl.dropdown');
- $dropdown.dropdown('toggle');
- }
- return $loading.fadeOut();
- });
- };
- $block.on('click', '.js-remove-due-date', function(e) {
- e.preventDefault();
- $("input[name='" + fieldName + "']").val('');
- return addDueDate(false);
- });
- return $datePicker.datepicker({
- dateFormat: 'yy-mm-dd',
- defaultDate: $("input[name='" + fieldName + "']").val(),
- altField: "input[name='" + fieldName + "']",
- onSelect: function() {
- return addDueDate(true);
- }
- });
- });
- $(document).off('click', '.ui-datepicker-header a').on('click', '.ui-datepicker-header a', function(e) {
- return e.stopImmediatePropagation();
- });
- }
-
- return DueDateSelect;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/due_date_select.js.es6 b/app/assets/javascripts/due_date_select.js.es6
new file mode 100644
index 00000000000..e84f5ac9183
--- /dev/null
+++ b/app/assets/javascripts/due_date_select.js.es6
@@ -0,0 +1,179 @@
+/* eslint-disable */
+(function(global) {
+ class DueDateSelect {
+ constructor({ $dropdown, $loading } = {}) {
+ const $dropdownParent = $dropdown.closest('.dropdown');
+ const $block = $dropdown.closest('.block');
+ this.$loading = $loading;
+ this.$dropdown = $dropdown;
+ this.$dropdownParent = $dropdownParent;
+ this.$datePicker = $dropdownParent.find('.js-due-date-calendar');
+ this.$block = $block;
+ this.$selectbox = $dropdown.closest('.selectbox');
+ this.$value = $block.find('.value');
+ this.$valueContent = $block.find('.value-content');
+ this.$sidebarValue = $('.js-due-date-sidebar-value', $block);
+ this.fieldName = $dropdown.data('field-name'),
+ this.abilityName = $dropdown.data('ability-name'),
+ this.issueUpdateURL = $dropdown.data('issue-update')
+
+ this.rawSelectedDate = null;
+ this.displayedDate = null;
+ this.datePayload = null;
+
+ this.initGlDropdown();
+ this.initRemoveDueDate();
+ this.initDatePicker();
+ this.initStopPropagation();
+ }
+
+ initGlDropdown() {
+ this.$dropdown.glDropdown({
+ hidden: () => {
+ this.$selectbox.hide();
+ this.$value.css('display', '');
+ }
+ });
+ }
+
+ initDatePicker() {
+ this.$datePicker.datepicker({
+ dateFormat: 'yy-mm-dd',
+ defaultDate: $("input[name='" + this.fieldName + "']").val(),
+ altField: "input[name='" + this.fieldName + "']",
+ onSelect: () => {
+ if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
+ gl.issueBoards.BoardsStore.detail.issue.dueDate = $(`input[name='${this.fieldName}']`).val();
+ this.updateIssueBoardIssue();
+ } else {
+ return this.saveDueDate(true);
+ }
+ }
+ });
+ }
+
+ initRemoveDueDate() {
+ this.$block.on('click', '.js-remove-due-date', (e) => {
+ e.preventDefault();
+
+ if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
+ gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
+ this.updateIssueBoardIssue();
+ } else {
+ $("input[name='" + this.fieldName + "']").val('');
+ return this.saveDueDate(false);
+ }
+ });
+ }
+
+ initStopPropagation() {
+ $(document).off('click', '.ui-datepicker-header a').on('click', '.ui-datepicker-header a', (e) => {
+ return e.stopImmediatePropagation();
+ });
+ }
+
+ saveDueDate(isDropdown) {
+ this.parseSelectedDate();
+ this.prepSelectedDate();
+ this.submitSelectedDate(isDropdown);
+ }
+
+ parseSelectedDate() {
+ this.rawSelectedDate = $("input[name='" + this.fieldName + "']").val();
+ if (this.rawSelectedDate.length) {
+ let dateObj = new Date(this.rawSelectedDate);
+ this.displayedDate = $.datepicker.formatDate('M d, yy', dateObj);
+ } else {
+ this.displayedDate = 'No due date';
+ }
+ }
+
+ prepSelectedDate() {
+ const datePayload = {};
+ datePayload[this.abilityName] = {};
+ datePayload[this.abilityName].due_date = this.rawSelectedDate;
+ this.datePayload = datePayload;
+ }
+
+ updateIssueBoardIssue () {
+ this.$loading.fadeIn();
+ this.$dropdown.trigger('loading.gl.dropdown');
+ this.$selectbox.hide();
+ this.$value.css('display', '');
+
+ gl.issueBoards.BoardsStore.detail.issue.update(this.$dropdown.attr('data-issue-update'))
+ .then(() => {
+ this.$loading.fadeOut();
+ });
+ }
+
+ submitSelectedDate(isDropdown) {
+ return $.ajax({
+ type: 'PUT',
+ url: this.issueUpdateURL,
+ data: this.datePayload,
+ dataType: 'json',
+ beforeSend: () => {
+ const selectedDateValue = this.datePayload[this.abilityName].due_date;
+ const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value';
+
+ this.$loading.fadeIn();
+
+ if (isDropdown) {
+ this.$dropdown.trigger('loading.gl.dropdown');
+ this.$selectbox.hide();
+ }
+
+ this.$value.css('display', '');
+ this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`);
+ this.$sidebarValue.html(this.displayedDate);
+
+ return selectedDateValue.length ?
+ $('.js-remove-due-date-holder').removeClass('hidden') :
+ $('.js-remove-due-date-holder').addClass('hidden');
+
+ }
+ }).done((data) => {
+ if (isDropdown) {
+ this.$dropdown.trigger('loaded.gl.dropdown');
+ this.$dropdown.dropdown('toggle');
+ }
+ return this.$loading.fadeOut();
+ });
+ }
+ }
+
+ class DueDateSelectors {
+ constructor() {
+ this.initMilestoneDatePicker();
+ this.initIssuableSelect();
+ }
+
+ initMilestoneDatePicker() {
+ $('.datepicker').datepicker({
+ dateFormat: 'yy-mm-dd'
+ });
+
+ $('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
+ e.preventDefault();
+ const datepicker = $(e.target).siblings('.datepicker');
+ $.datepicker._clearDate(datepicker);
+ });
+ }
+
+ initIssuableSelect() {
+ const $loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
+
+ $('.js-due-date-select').each((i, dropdown) => {
+ const $dropdown = $(dropdown);
+ new DueDateSelect({
+ $dropdown,
+ $loading
+ });
+ });
+ }
+ }
+
+ global.DueDateSelectors = DueDateSelectors;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6
new file mode 100644
index 00000000000..35e183a9086
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment.js.es6
@@ -0,0 +1,249 @@
+/* eslint-disable no-param-reassign */
+/* global Vue */
+/* global EnvironmentsService */
+
+//= require vue
+//= require vue-resource
+//= require_tree ../services/
+//= require ./environment_item
+
+(() => {
+ window.gl = window.gl || {};
+
+ /**
+ * Given the visibility prop provided by the url query parameter and which
+ * changes according to the active tab we need to filter which environments
+ * should be visible.
+ *
+ * The environments array is a recursive tree structure and we need to filter
+ * both root level environments and children environments.
+ *
+ * In order to acomplish that, both `filterState` and `filterEnvironmnetsByState`
+ * functions work together.
+ * The first one works as the filter that verifies if the given environment matches
+ * the given state.
+ * The second guarantees both root level and children elements are filtered as well.
+ */
+
+ const filterState = state => environment => environment.state === state && environment;
+ /**
+ * Given the filter function and the array of environments will return only
+ * the environments that match the state provided to the filter function.
+ *
+ * @param {Function} fn
+ * @param {Array} array
+ * @return {Array}
+ */
+ const filterEnvironmnetsByState = (fn, arr) => arr.map((item) => {
+ if (item.children) {
+ const filteredChildren = filterEnvironmnetsByState(fn, item.children).filter(Boolean);
+ if (filteredChildren.length) {
+ item.children = filteredChildren;
+ return item;
+ }
+ }
+ return fn(item);
+ }).filter(Boolean);
+
+ window.gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', {
+ props: {
+ store: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+
+ components: {
+ 'environment-item': window.gl.environmentsList.EnvironmentItem,
+ },
+
+ data() {
+ const environmentsData = document.querySelector('#environments-list-view').dataset;
+
+ return {
+ state: this.store.state,
+ visibility: 'available',
+ isLoading: false,
+ cssContainerClass: environmentsData.cssClass,
+ endpoint: environmentsData.environmentsDataEndpoint,
+ canCreateDeployment: environmentsData.canCreateDeployment,
+ canReadEnvironment: environmentsData.canReadEnvironment,
+ canCreateEnvironment: environmentsData.canCreateEnvironment,
+ projectEnvironmentsPath: environmentsData.projectEnvironmentsPath,
+ projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath,
+ newEnvironmentPath: environmentsData.newEnvironmentPath,
+ helpPagePath: environmentsData.helpPagePath,
+ };
+ },
+
+ computed: {
+ filteredEnvironments() {
+ return filterEnvironmnetsByState(filterState(this.visibility), this.state.environments);
+ },
+
+ scope() {
+ return this.$options.getQueryParameter('scope');
+ },
+
+ canReadEnvironmentParsed() {
+ return this.$options.convertPermissionToBoolean(this.canReadEnvironment);
+ },
+
+ canCreateDeploymentParsed() {
+ return this.$options.convertPermissionToBoolean(this.canCreateDeployment);
+ },
+
+ canCreateEnvironmentParsed() {
+ return this.$options.convertPermissionToBoolean(this.canCreateEnvironment);
+ },
+ },
+
+ /**
+ * Fetches all the environmnets and stores them.
+ * Toggles loading property.
+ */
+ created() {
+ gl.environmentsService = new EnvironmentsService(this.endpoint);
+
+ const scope = this.$options.getQueryParameter('scope');
+ if (scope) {
+ this.visibility = scope;
+ }
+
+ this.isLoading = true;
+
+ return gl.environmentsService.all()
+ .then(resp => resp.json())
+ .then((json) => {
+ this.store.storeEnvironments(json);
+ this.isLoading = false;
+ });
+ },
+
+ /**
+ * Transforms the url parameter into an object and
+ * returns the one requested.
+ *
+ * @param {String} param
+ * @returns {String} The value of the requested parameter.
+ */
+ getQueryParameter(parameter) {
+ return window.location.search.substring(1).split('&').reduce((acc, param) => {
+ const paramSplited = param.split('=');
+ acc[paramSplited[0]] = paramSplited[1];
+ return acc;
+ }, {})[parameter];
+ },
+
+ /**
+ * Converts permission provided as strings to booleans.
+ * @param {String} string
+ * @returns {Boolean}
+ */
+ convertPermissionToBoolean(string) {
+ return string === 'true';
+ },
+
+ methods: {
+ toggleRow(model) {
+ return this.store.toggleFolder(model.name);
+ },
+ },
+
+ template: `
+ <div :class="cssContainerClass">
+ <div class="top-area">
+ <ul v-if="!isLoading" class="nav-links">
+ <li v-bind:class="{ 'active': scope === undefined }">
+ <a :href="projectEnvironmentsPath">
+ Available
+ <span class="badge js-available-environments-count">
+ {{state.availableCounter}}
+ </span>
+ </a>
+ </li>
+ <li v-bind:class="{ 'active' : scope === 'stopped' }">
+ <a :href="projectStoppedEnvironmentsPath">
+ Stopped
+ <span class="badge js-stopped-environments-count">
+ {{state.stoppedCounter}}
+ </span>
+ </a>
+ </li>
+ </ul>
+ <div v-if="canCreateEnvironmentParsed && !isLoading" class="nav-controls">
+ <a :href="newEnvironmentPath" class="btn btn-create">
+ New environment
+ </a>
+ </div>
+ </div>
+
+ <div class="environments-container">
+ <div class="environments-list-loading text-center" v-if="isLoading">
+ <i class="fa fa-spinner spin"></i>
+ </div>
+
+ <div class="blank-state blank-state-no-icon"
+ v-if="!isLoading && state.environments.length === 0">
+ <h2 class="blank-state-title">
+ You don't have any environments right now.
+ </h2>
+ <p class="blank-state-text">
+ Environments are places where code gets deployed, such as staging or production.
+ <br />
+ <a :href="helpPagePath">
+ Read more about environments
+ </a>
+ </p>
+
+ <a
+ v-if="canCreateEnvironmentParsed"
+ :href="newEnvironmentPath"
+ class="btn btn-create">
+ New Environment
+ </a>
+ </div>
+
+ <div class="table-holder"
+ v-if="!isLoading && state.environments.length > 0">
+ <table class="table ci-table environments">
+ <thead>
+ <tr>
+ <th class="environments-name">Environment</th>
+ <th class="environments-deploy">Last deployment</th>
+ <th class="environments-build">Build</th>
+ <th class="environments-commit">Commit</th>
+ <th class="environments-date"></th>
+ <th class="hidden-xs environments-actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <template v-for="model in filteredEnvironments"
+ v-bind:model="model">
+
+ <tr
+ is="environment-item"
+ :model="model"
+ :toggleRow="toggleRow.bind(model)"
+ :can-create-deployment="canCreateDeploymentParsed"
+ :can-read-environment="canReadEnvironmentParsed"></tr>
+
+ <tr v-if="model.isOpen && model.children && model.children.length > 0"
+ is="environment-item"
+ v-for="children in model.children"
+ :model="children"
+ :toggleRow="toggleRow.bind(children)"
+ :can-create-deployment="canCreateDeploymentParsed"
+ :can-read-environment="canReadEnvironmentParsed">
+ </tr>
+
+ </template>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ `,
+ });
+})();
diff --git a/app/assets/javascripts/environments/components/environment_actions.js.es6 b/app/assets/javascripts/environments/components/environment_actions.js.es6
new file mode 100644
index 00000000000..d149a446e0b
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_actions.js.es6
@@ -0,0 +1,67 @@
+/*= require vue */
+/* global Vue */
+
+(() => {
+ window.gl = window.gl || {};
+ window.gl.environmentsList = window.gl.environmentsList || {};
+
+ window.gl.environmentsList.ActionsComponent = Vue.component('actions-component', {
+ props: {
+ actions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+
+ /**
+ * Appends the svg icon that were render in the index page.
+ * In order to reuse the svg instead of copy and paste in this template
+ * we need to render it outside this component using =custom_icon partial.
+ *
+ * TODO: Remove this when webpack is merged.
+ *
+ */
+ mounted() {
+ const playIcon = document.querySelector('.play-icon-svg.hidden svg');
+
+ const dropdownContainer = this.$el.querySelector('.dropdown-play-icon-container');
+ const actionContainers = this.$el.querySelectorAll('.action-play-icon-container');
+ // Phantomjs does not have support to iterate a nodelist.
+ const actionsArray = [].slice.call(actionContainers);
+
+ if (playIcon && actionsArray && dropdownContainer) {
+ dropdownContainer.appendChild(playIcon.cloneNode(true));
+
+ actionsArray.forEach((element) => {
+ element.appendChild(playIcon.cloneNode(true));
+ });
+ }
+ },
+
+ template: `
+ <div class="inline">
+ <div class="dropdown">
+ <a class="dropdown-new btn btn-default" data-toggle="dropdown">
+ <span class="dropdown-play-icon-container"></span>
+ <i class="fa fa-caret-down"></i>
+ </a>
+
+ <ul class="dropdown-menu dropdown-menu-align-right">
+ <li v-for="action in actions">
+ <a :href="action.play_path"
+ data-method="post"
+ rel="nofollow"
+ class="js-manual-action-link">
+ <span class="action-play-icon-container"></span>
+ <span>
+ {{action.name}}
+ </span>
+ </a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ `,
+ });
+})();
diff --git a/app/assets/javascripts/environments/components/environment_external_url.js.es6 b/app/assets/javascripts/environments/components/environment_external_url.js.es6
new file mode 100644
index 00000000000..79cd5ded5bd
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_external_url.js.es6
@@ -0,0 +1,22 @@
+/*= require vue */
+/* global Vue */
+
+(() => {
+ window.gl = window.gl || {};
+ window.gl.environmentsList = window.gl.environmentsList || {};
+
+ window.gl.environmentsList.ExternalUrlComponent = Vue.component('external-url-component', {
+ props: {
+ external_url: {
+ type: String,
+ default: '',
+ },
+ },
+
+ template: `
+ <a class="btn external_url" :href="external_url" target="_blank">
+ <i class="fa fa-external-link"></i>
+ </a>
+ `,
+ });
+})();
diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6
new file mode 100644
index 00000000000..07f49cce3dc
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_item.js.es6
@@ -0,0 +1,497 @@
+/* global Vue */
+/* global timeago */
+
+/*= require timeago */
+/*= require lib/utils/text_utility */
+/*= require vue_common_component/commit */
+/*= require ./environment_actions */
+/*= require ./environment_external_url */
+/*= require ./environment_stop */
+/*= require ./environment_rollback */
+
+(() => {
+ /**
+ * Envrionment Item Component
+ *
+ * Used in a hierarchical structure to show folders with children
+ * in a table.
+ * Recursive component based on [Tree View](https://vuejs.org/examples/tree-view.html)
+ *
+ * See this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/22539)
+ * for more information.15
+ */
+
+ window.gl = window.gl || {};
+ window.gl.environmentsList = window.gl.environmentsList || {};
+
+ gl.environmentsList.EnvironmentItem = Vue.component('environment-item', {
+
+ components: {
+ 'commit-component': window.gl.CommitComponent,
+ 'actions-component': window.gl.environmentsList.ActionsComponent,
+ 'external-url-component': window.gl.environmentsList.ExternalUrlComponent,
+ 'stop-component': window.gl.environmentsList.StopComponent,
+ 'rollback-component': window.gl.environmentsList.RollbackComponent,
+ },
+
+ props: {
+ model: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+
+ toggleRow: {
+ type: Function,
+ required: false,
+ },
+
+ canCreateDeployment: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+
+ canReadEnvironment: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+
+ data() {
+ return {
+ rowClass: {
+ 'children-row': this.model['vue-isChildren'],
+ },
+ };
+ },
+
+ computed: {
+
+ /**
+ * If an item has a `children` entry it means it is a folder.
+ * Folder items have different behaviours - it is possible to toggle
+ * them and show their children.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ isFolder() {
+ return this.model.children && this.model.children.length > 0;
+ },
+
+ /**
+ * If an item is inside a folder structure will return true.
+ * Used for css purposes.
+ *
+ * @returns {Boolean|undefined}
+ */
+ isChildren() {
+ return this.model['vue-isChildren'];
+ },
+
+ /**
+ * Counts the number of environments in each folder.
+ * Used to show a badge with the counter.
+ *
+ * @returns {Number|Undefined} The number of environments for the current folder.
+ */
+ childrenCounter() {
+ return this.model.children && this.model.children.length;
+ },
+
+ /**
+ * Verifies if `last_deployment` key exists in the current Envrionment.
+ * This key is required to render most of the html - this method works has
+ * an helper.
+ *
+ * @returns {Boolean}
+ */
+ hasLastDeploymentKey() {
+ if (this.model.last_deployment &&
+ !this.$options.isObjectEmpty(this.model.last_deployment)) {
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * 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.last_deployment && this.model.last_deployment.manual_actions &&
+ this.model.last_deployment.manual_actions.length > 0;
+ },
+
+ /**
+ * Returns the value of the `stoppable?` key provided in the response.
+ *
+ * @returns {Boolean}
+ */
+ isStoppable() {
+ return this.model['stoppable?'];
+ },
+
+ /**
+ * Verifies if the `deployable` key is present in `last_deployment` key.
+ * Used to verify whether we should or not render the rollback partial.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ canRetry() {
+ return this.hasLastDeploymentKey &&
+ this.model.last_deployment &&
+ this.model.last_deployment.deployable;
+ },
+
+ /**
+ * Human readable date.
+ *
+ * @returns {String}
+ */
+ createdDate() {
+ const timeagoInstance = new timeago(); // eslint-disable-line
+
+ return timeagoInstance.format(this.model.created_at);
+ },
+
+ /**
+ * 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: gl.text.humanize(action.name),
+ play_path: action.play_path,
+ };
+ return parsedAction;
+ });
+ }
+ return [];
+ },
+
+ /**
+ * Builds the string used in the user image alt attribute.
+ *
+ * @returns {String}
+ */
+ userImageAltDescription() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.user &&
+ this.model.last_deployment.user.username) {
+ return `${this.model.last_deployment.user.username}'s avatar'`;
+ }
+ return '';
+ },
+
+ /**
+ * If provided, returns the commit tag.
+ *
+ * @returns {String|Undefined}
+ */
+ commitTag() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.tag) {
+ return this.model.last_deployment.tag;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit ref.
+ *
+ * @returns {Object|Undefined}
+ */
+ commitRef() {
+ if (this.model.last_deployment && this.model.last_deployment.ref) {
+ return this.model.last_deployment.ref;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit url.
+ *
+ * @returns {String|Undefined}
+ */
+ commitUrl() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.commit_path) {
+ return this.model.last_deployment.commit.commit_path;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit short sha.
+ *
+ * @returns {String|Undefined}
+ */
+ commitShortSha() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.short_id) {
+ return this.model.last_deployment.commit.short_id;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit title.
+ *
+ * @returns {String|Undefined}
+ */
+ commitTitle() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.title) {
+ return this.model.last_deployment.commit.title;
+ }
+ return undefined;
+ },
+
+ /**
+ * If provided, returns the commit tag.
+ *
+ * @returns {Object|Undefined}
+ */
+ commitAuthor() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.commit &&
+ this.model.last_deployment.commit.author) {
+ return this.model.last_deployment.commit.author;
+ }
+
+ return undefined;
+ },
+
+ /**
+ * Verifies if the `retry_path` key is present and returns its value.
+ *
+ * @returns {String|Undefined}
+ */
+ retryUrl() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.deployable &&
+ this.model.last_deployment.deployable.retry_path) {
+ return this.model.last_deployment.deployable.retry_path;
+ }
+ return undefined;
+ },
+
+ /**
+ * Verifies if the `last?` key is present and returns its value.
+ *
+ * @returns {Boolean|Undefined}
+ */
+ isLastDeployment() {
+ return this.model.last_deployment && this.model.last_deployment['last?'];
+ },
+
+ /**
+ * Builds the name of the builds needed to display both the name and the id.
+ *
+ * @returns {String}
+ */
+ buildName() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.deployable) {
+ return `${this.model.last_deployment.deployable.name} #${this.model.last_deployment.deployable.id}`;
+ }
+ return '';
+ },
+
+ /**
+ * Builds the needed string to show the internal id.
+ *
+ * @returns {String}
+ */
+ deploymentInternalId() {
+ if (this.model.last_deployment &&
+ this.model.last_deployment.iid) {
+ return `#${this.model.last_deployment.iid}`;
+ }
+ return '';
+ },
+
+ /**
+ * Verifies if the user object is present under last_deployment object.
+ *
+ * @returns {Boolean}
+ */
+ deploymentHasUser() {
+ return !this.$options.isObjectEmpty(this.model.last_deployment) &&
+ !this.$options.isObjectEmpty(this.model.last_deployment.user);
+ },
+
+ /**
+ * Returns the user object nested with the last_deployment object.
+ * Used to render the template.
+ *
+ * @returns {Object}
+ */
+ deploymentUser() {
+ if (!this.$options.isObjectEmpty(this.model.last_deployment) &&
+ !this.$options.isObjectEmpty(this.model.last_deployment.user)) {
+ return this.model.last_deployment.user;
+ }
+ return {};
+ },
+
+ /**
+ * Verifies if the build name column should be rendered by verifing
+ * if all the information needed is present
+ * and if the environment is not a folder.
+ *
+ * @returns {Boolean}
+ */
+ shouldRenderBuildName() {
+ return !this.isFolder &&
+ !this.$options.isObjectEmpty(this.model.last_deployment) &&
+ !this.$options.isObjectEmpty(this.model.last_deployment.deployable);
+ },
+
+ /**
+ * Verifies if deplyment internal ID should be rendered by verifing
+ * if all the information needed is present
+ * and if the environment is not a folder.
+ *
+ * @returns {Boolean}
+ */
+ shouldRenderDeploymentID() {
+ return !this.isFolder &&
+ !this.$options.isObjectEmpty(this.model.last_deployment) &&
+ this.model.last_deployment.iid !== undefined;
+ },
+ },
+
+ /**
+ * Helper to verify if certain given object are empty.
+ * Should be replaced by lodash _.isEmpty - https://lodash.com/docs/4.17.2#isEmpty
+ * @param {Object} object
+ * @returns {Bollean}
+ */
+ isObjectEmpty(object) {
+ for (const key in object) { // eslint-disable-line
+ if (hasOwnProperty.call(object, key)) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ template: `
+ <tr>
+ <td v-bind:class="{ 'children-row': isChildren}">
+ <a v-if="!isFolder"
+ class="environment-name"
+ :href="model.environment_path">
+ {{model.name}}
+ </a>
+ <span v-else v-on:click="toggleRow(model)" class="folder-name">
+ <span class="folder-icon">
+ <i v-show="model.isOpen" class="fa fa-caret-down"></i>
+ <i v-show="!model.isOpen" class="fa fa-caret-right"></i>
+ </span>
+
+ <span>
+ {{model.name}}
+ </span>
+
+ <span class="badge">
+ {{childrenCounter}}
+ </span>
+ </span>
+ </td>
+
+ <td class="deployment-column">
+ <span v-if="shouldRenderDeploymentID">
+ {{deploymentInternalId}}
+ </span>
+
+ <span v-if="!isFolder && deploymentHasUser">
+ by
+ <a :href="deploymentUser.web_url" class="js-deploy-user-container">
+ <img class="avatar has-tooltip s20"
+ :src="deploymentUser.avatar_url"
+ :alt="userImageAltDescription"
+ :title="deploymentUser.username" />
+ </a>
+ </span>
+ </td>
+
+ <td>
+ <a v-if="shouldRenderBuildName"
+ class="build-link"
+ :href="model.last_deployment.deployable.build_path">
+ {{buildName}}
+ </a>
+ </td>
+
+ <td>
+ <div v-if="!isFolder && hasLastDeploymentKey" class="js-commit-component">
+ <commit-component
+ :tag="commitTag"
+ :ref="commitRef"
+ :commit_url="commitUrl"
+ :short_sha="commitShortSha"
+ :title="commitTitle"
+ :author="commitAuthor">
+ </commit-component>
+ </div>
+ <p v-if="!isFolder && !hasLastDeploymentKey" class="commit-title">
+ No deployments yet
+ </p>
+ </td>
+
+ <td>
+ <span
+ v-if="!isFolder && model.last_deployment"
+ class="environment-created-date-timeago">
+ {{createdDate}}
+ </span>
+ </td>
+
+ <td class="hidden-xs">
+ <div v-if="!isFolder">
+ <div v-if="hasManualActions && canCreateDeployment"
+ class="inline js-manual-actions-container">
+ <actions-component
+ :actions="manualActions">
+ </actions-component>
+ </div>
+
+ <div v-if="model.external_url && canReadEnvironment"
+ class="inline js-external-url-container">
+ <external-url-component
+ :external_url="model.external_url">
+ </external_url-component>
+ </div>
+
+ <div v-if="isStoppable && canCreateDeployment"
+ class="inline js-stop-component-container">
+ <stop-component
+ :stop_url="model.stop_path">
+ </stop-component>
+ </div>
+
+ <div v-if="canRetry && canCreateDeployment"
+ class="inline js-rollback-component-container">
+ <rollback-component
+ :is_last_deployment="isLastDeployment"
+ :retry_url="retryUrl">
+ </rollback-component>
+ </div>
+ </div>
+ </td>
+ </tr>
+ `,
+ });
+})();
diff --git a/app/assets/javascripts/environments/components/environment_rollback.js.es6 b/app/assets/javascripts/environments/components/environment_rollback.js.es6
new file mode 100644
index 00000000000..55e5c826e07
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_rollback.js.es6
@@ -0,0 +1,31 @@
+/*= require vue */
+/* global Vue */
+
+(() => {
+ window.gl = window.gl || {};
+ window.gl.environmentsList = window.gl.environmentsList || {};
+
+ window.gl.environmentsList.RollbackComponent = Vue.component('rollback-component', {
+ props: {
+ retry_url: {
+ type: String,
+ default: '',
+ },
+ is_last_deployment: {
+ type: Boolean,
+ default: true,
+ },
+ },
+
+ template: `
+ <a class="btn" :href="retry_url" data-method="post" rel="nofollow">
+ <span v-if="is_last_deployment">
+ Re-deploy
+ </span>
+ <span v-else>
+ Rollback
+ </span>
+ </a>
+ `,
+ });
+})();
diff --git a/app/assets/javascripts/environments/components/environment_stop.js.es6 b/app/assets/javascripts/environments/components/environment_stop.js.es6
new file mode 100644
index 00000000000..e6d66a0148c
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_stop.js.es6
@@ -0,0 +1,26 @@
+/*= require vue */
+/* global Vue */
+
+(() => {
+ window.gl = window.gl || {};
+ window.gl.environmentsList = window.gl.environmentsList || {};
+
+ window.gl.environmentsList.StopComponent = Vue.component('stop-component', {
+ props: {
+ stop_url: {
+ type: String,
+ default: '',
+ },
+ },
+
+ template: `
+ <a class="btn stop-env-link"
+ :href="stop_url"
+ data-confirm="Are you sure you want to stop this environment?"
+ data-method="post"
+ rel="nofollow">
+ <i class="fa fa-stop stop-env-icon"></i>
+ </a>
+ `,
+ });
+})();
diff --git a/app/assets/javascripts/environments/environments_bundle.js.es6 b/app/assets/javascripts/environments/environments_bundle.js.es6
new file mode 100644
index 00000000000..20eee7976ec
--- /dev/null
+++ b/app/assets/javascripts/environments/environments_bundle.js.es6
@@ -0,0 +1,21 @@
+//= require vue
+//= require_tree ./stores/
+//= require ./components/environment
+//= require ./vue_resource_interceptor
+
+
+$(() => {
+ window.gl = window.gl || {};
+
+ if (window.gl.EnvironmentsListApp) {
+ window.gl.EnvironmentsListApp.$destroy(true);
+ }
+ const Store = window.gl.environmentsList.EnvironmentsStore;
+
+ window.gl.EnvironmentsListApp = new window.gl.environmentsList.EnvironmentsComponent({
+ el: document.querySelector('#environments-list-view'),
+ propsData: {
+ store: Store.create(),
+ },
+ });
+});
diff --git a/app/assets/javascripts/environments/services/environments_service.js.es6 b/app/assets/javascripts/environments/services/environments_service.js.es6
new file mode 100644
index 00000000000..15ec7b76c3d
--- /dev/null
+++ b/app/assets/javascripts/environments/services/environments_service.js.es6
@@ -0,0 +1,22 @@
+/* globals Vue */
+/* eslint-disable no-unused-vars, no-param-reassign */
+class EnvironmentsService {
+
+ constructor(root) {
+ Vue.http.options.root = root;
+
+ this.environments = Vue.resource(root);
+
+ Vue.http.interceptors.push((request, next) => {
+ // needed in order to not break the tests.
+ if ($.rails) {
+ request.headers['X-CSRF-Token'] = $.rails.csrfToken();
+ }
+ next();
+ });
+ }
+
+ all() {
+ return this.environments.get();
+ }
+}
diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6
new file mode 100644
index 00000000000..0204a903ab5
--- /dev/null
+++ b/app/assets/javascripts/environments/stores/environments_store.js.es6
@@ -0,0 +1,131 @@
+/* eslint-disable no-param-reassign */
+(() => {
+ window.gl = window.gl || {};
+ window.gl.environmentsList = window.gl.environmentsList || {};
+
+ gl.environmentsList.EnvironmentsStore = {
+ state: {},
+
+ create() {
+ this.state.environments = [];
+ this.state.stoppedCounter = 0;
+ this.state.availableCounter = 0;
+
+ return this;
+ },
+
+ /**
+ * In order to display a tree view we need to modify the received
+ * data in to a tree structure based on `environment_type`
+ * sorted alphabetically.
+ * In each children a `vue-` property will be added. This property will be
+ * used to know if an item is a children mostly for css purposes. This is
+ * needed because the children row is a fragment instance and therfore does
+ * not accept non-prop attributes.
+ *
+ *
+ * @example
+ * it will transform this:
+ * [
+ * { name: "environment", environment_type: "review" },
+ * { name: "environment_1", environment_type: null }
+ * { name: "environment_2, environment_type: "review" }
+ * ]
+ * into this:
+ * [
+ * { name: "review", children:
+ * [
+ * { name: "environment", environment_type: "review", vue-isChildren: true},
+ * { name: "environment_2", environment_type: "review", vue-isChildren: true}
+ * ]
+ * },
+ * {name: "environment_1", environment_type: null}
+ * ]
+ *
+ *
+ * @param {Array} environments List of environments.
+ * @returns {Array} Tree structured array with the received environments.
+ */
+ storeEnvironments(environments = []) {
+ this.state.stoppedCounter = this.countByState(environments, 'stopped');
+ this.state.availableCounter = this.countByState(environments, 'available');
+
+ const environmentsTree = environments.reduce((acc, environment) => {
+ if (environment.environment_type !== null) {
+ const occurs = acc.filter(element => element.children &&
+ element.name === environment.environment_type);
+
+ environment['vue-isChildren'] = true;
+
+ if (occurs.length) {
+ acc[acc.indexOf(occurs[0])].children.push(environment);
+ acc[acc.indexOf(occurs[0])].children.sort(this.sortByName);
+ } else {
+ acc.push({
+ name: environment.environment_type,
+ children: [environment],
+ isOpen: false,
+ 'vue-isChildren': environment['vue-isChildren'],
+ });
+ }
+ } else {
+ acc.push(environment);
+ }
+
+ return acc;
+ }, []).sort(this.sortByName);
+
+ this.state.environments = environmentsTree;
+
+ return environmentsTree;
+ },
+
+ /**
+ * Toggles folder open property given the environment type.
+ *
+ * @param {String} envType
+ * @return {Array}
+ */
+ toggleFolder(envType) {
+ const environments = this.state.environments;
+
+ const environmentsCopy = environments.map((env) => {
+ if (env['vue-isChildren'] && env.name === envType) {
+ env.isOpen = !env.isOpen;
+ }
+
+ return env;
+ });
+
+ this.state.environments = environmentsCopy;
+
+ return environmentsCopy;
+ },
+
+ /**
+ * Given an array of environments, returns the number of environments
+ * that have the given state.
+ *
+ * @param {Array} environments
+ * @param {String} state
+ * @returns {Number}
+ */
+ countByState(environments, state) {
+ return environments.filter(env => env.state === state).length;
+ },
+
+ /**
+ * Sorts the two objects provided by their name.
+ *
+ * @param {Object} a
+ * @param {Object} b
+ * @returns {Number}
+ */
+ sortByName(a, b) {
+ const nameA = a.name.toUpperCase();
+ const nameB = b.name.toUpperCase();
+
+ return nameA < nameB ? -1 : nameA > nameB ? 1 : 0; // eslint-disable-line
+ },
+ };
+})();
diff --git a/app/assets/javascripts/environments/vue_resource_interceptor.js.es6 b/app/assets/javascripts/environments/vue_resource_interceptor.js.es6
new file mode 100644
index 00000000000..406bdbc1c7d
--- /dev/null
+++ b/app/assets/javascripts/environments/vue_resource_interceptor.js.es6
@@ -0,0 +1,12 @@
+/* global Vue */
+Vue.http.interceptors.push((request, next) => {
+ Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
+
+ next((response) => {
+ if (typeof response.data === 'string') {
+ response.data = JSON.parse(response.data); // eslint-disable-line
+ }
+
+ Vue.activeResources--; // eslint-disable-line
+ });
+});
diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js
index 24f9e00097c..fc6c130113d 100644
--- a/app/assets/javascripts/extensions/array.js
+++ b/app/assets/javascripts/extensions/array.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */
Array.prototype.first = function() {
return this[0];
}
diff --git a/app/assets/javascripts/extensions/element.js.es6 b/app/assets/javascripts/extensions/element.js.es6
new file mode 100644
index 00000000000..6d9b0c4bc3e
--- /dev/null
+++ b/app/assets/javascripts/extensions/element.js.es6
@@ -0,0 +1,9 @@
+/* global Element */
+/* eslint-disable consistent-return, max-len */
+
+Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
+
+Element.prototype.closest = Element.prototype.closest || function closest(selector, selectedElement = this) {
+ if (!selectedElement) return;
+ return selectedElement.matches(selector) ? selectedElement : Element.prototype.closest(selector, selectedElement.parentElement);
+};
diff --git a/app/assets/javascripts/extensions/jquery.js b/app/assets/javascripts/extensions/jquery.js
index 4978e24949c..cdedc865d1b 100644
--- a/app/assets/javascripts/extensions/jquery.js
+++ b/app/assets/javascripts/extensions/jquery.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, object-shorthand, comma-dangle, padded-blocks, max-len */
// Disable an element and add the 'disabled' Bootstrap class
(function() {
$.fn.extend({
diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js
index 3fb3b1a8b51..0122e847161 100644
--- a/app/assets/javascripts/files_comment_button.js
+++ b/app/assets/javascripts/files_comment_button.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, max-len, one-var, one-var-declaration-per-line, quotes, prefer-template, newline-per-chained-call, comma-dangle, new-cap, no-else-return, padded-blocks, consistent-return, no-undef, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index c8a02d6fa15..804d7d9c4ab 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-param-reassign, quotes, quote-props, prefer-template, comma-dangle, padded-blocks, max-len */
(function() {
this.Flash = (function() {
var hideFlash;
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index d0786bf0053..5d9ac4d350a 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
// Creates the variables for setting up GFM auto-completion
(function() {
if (window.GitLab == null) {
@@ -15,7 +16,7 @@
},
// Team Members
Members: {
- template: '<li>${username} <small>${title}</small></li>'
+ template: '<li>${avatarTag} ${username} <small>${title}</small></li>'
},
Labels: {
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
@@ -33,6 +34,8 @@
},
DefaultOptions: {
sorter: function(query, items, searchKey) {
+ // Highlight first item only if at least one char was typed
+ this.setting.highlightFirst = this.setting.alwaysHighlightFirst || query.length > 0;
if ((items[0].name != null) && items[0].name === 'loading') {
return items;
}
@@ -52,37 +55,27 @@
}
}
},
- setup: function(input) {
+ setup: _.debounce(function(input) {
// Add GFM auto-completion to all input fields, that accept GFM input.
this.input = input || $('.js-gfm-input');
// destroy previous instances
this.destroyAtWho();
// set up instances
this.setupAtWho();
- if (this.dataSource) {
- if (!this.dataLoading && !this.cachedData) {
- this.dataLoading = true;
- setTimeout((function(_this) {
- return function() {
- var fetch;
- fetch = _this.fetchData(_this.dataSource);
- return fetch.done(function(data) {
- _this.dataLoading = false;
- return _this.loadData(data);
- });
- };
- // We should wait until initializations are done
- // and only trigger the last .setup since
- // The previous .dataSource belongs to the previous issuable
- // and the last one will have the **proper** .dataSource property
- // TODO: Make this a singleton and turn off events when moving to another page
- })(this), 1000);
- }
- if (this.cachedData != null) {
- return this.loadData(this.cachedData);
- }
+
+ if (this.dataSource && !this.dataLoading && !this.cachedData) {
+ this.dataLoading = true;
+ return this.fetchData(this.dataSource)
+ .done((data) => {
+ this.dataLoading = false;
+ this.loadData(data);
+ });
+ };
+
+ if (this.cachedData != null) {
+ return this.loadData(this.cachedData);
}
- },
+ }, 1000),
setupAtWho: function() {
// Emoji
this.input.atwho({
@@ -119,13 +112,14 @@
insertTpl: '${atwho-at}${username}',
searchKey: 'search',
data: ['loading'],
+ alwaysHighlightFirst: true,
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
beforeInsert: this.DefaultOptions.beforeInsert,
beforeSave: function(members) {
return $.map(members, function(m) {
- var title;
+ let title = '';
if (m.username == null) {
return m;
}
@@ -133,10 +127,16 @@
if (m.count) {
title += " (" + m.count + ")";
}
+
+ 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 txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
+
return {
username: m.username,
- title: sanitize(title),
- search: sanitize(m.username + " " + m.name)
+ avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
+ title: gl.utils.sanitize(title),
+ search: gl.utils.sanitize(m.username + " " + m.name)
};
});
}
@@ -168,7 +168,7 @@
}
return {
id: i.iid,
- title: sanitize(i.title),
+ title: gl.utils.sanitize(i.title),
search: i.iid + " " + i.title
};
});
@@ -191,6 +191,7 @@
insertTpl: '${atwho-at}"${title}"',
data: ['loading'],
callbacks: {
+ sorter: this.DefaultOptions.sorter,
beforeSave: function(milestones) {
return $.map(milestones, function(m) {
if (m.title == null) {
@@ -198,7 +199,7 @@
}
return {
id: m.iid,
- title: sanitize(m.title),
+ title: gl.utils.sanitize(m.title),
search: "" + m.title
};
});
@@ -231,7 +232,7 @@
}
return {
id: m.iid,
- title: sanitize(m.title),
+ title: gl.utils.sanitize(m.title),
search: m.iid + " " + m.title
};
});
@@ -245,13 +246,14 @@
displayTpl: this.Labels.template,
insertTpl: '${atwho-at}${title}',
callbacks: {
+ sorter: this.DefaultOptions.sorter,
beforeSave: function(merges) {
var sanitizeLabelTitle;
sanitizeLabelTitle = function(title) {
if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) {
- return "\"" + (sanitize(title)) + "\"";
+ return "\"" + (gl.utils.sanitize(title)) + "\"";
} else {
- return sanitize(title);
+ return gl.utils.sanitize(title);
}
};
return $.map(merges, function(m) {
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 087f27d9f4c..969778dded7 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, space-before-blocks, prefer-rest-params, max-len, vars-on-top, no-plusplus, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, semi, no-return-assign, no-else-return, camelcase, no-undef, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, padded-blocks, prefer-template, no-param-reassign, no-loop-func, no-extra-semi, keyword-spacing, no-mixed-operators, max-len */
(function() {
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
@@ -25,7 +26,7 @@
return function(e) {
e.preventDefault();
e.stopPropagation();
- return _this.input.val('').trigger('keyup').focus();
+ return _this.input.val('').trigger('input').focus();
};
})(this));
// Key events
@@ -37,28 +38,16 @@
e.preventDefault()
}
})
- .on('keyup', function(e) {
- var keyCode;
- keyCode = e.which;
- if (ARROW_KEY_CODES.indexOf(keyCode) >= 0) {
- return;
- }
+ .on('input', function() {
if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.addClass(HAS_VALUE_CLASS);
} else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.removeClass(HAS_VALUE_CLASS);
}
- if (keyCode === 13 && !options.elIsInput) {
- return false;
- }
// Only filter asynchronously only if option remote is set
if (this.options.remote) {
clearTimeout(timeout);
return timeout = setTimeout(function() {
- var blurField = this.shouldBlur(keyCode);
- if (blurField && this.filterInputBlur) {
- this.input.blur();
- }
return this.options.query(this.input.val(), function(data) {
return this.options.callback(data);
}.bind(this));
@@ -220,7 +209,7 @@
FILTER_INPUT = '.dropdown-input .dropdown-input-field';
function GitLabDropdown(el1, options) {
- var ref, ref1, ref2, ref3, searchFields, selector, self;
+ var searchFields, selector, self;
this.el = el1;
this.options = options;
this.updateLabel = bind(this.updateLabel, this);
@@ -231,7 +220,11 @@
selector = $(this.el).data("target");
this.dropdown = selector != null ? $(selector) : $(this.el).parent();
// Set Defaults
- ref = this.options, this.filterInput = (ref1 = ref.filterInput) != null ? ref1 : this.getElement(FILTER_INPUT), this.highlight = (ref2 = ref.highlight) != null ? ref2 : false, this.filterInputBlur = (ref3 = ref.filterInputBlur) != null ? ref3 : true;
+ this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT);
+ this.highlight = !!this.options.highlight
+ this.filterInputBlur = this.options.filterInputBlur != null
+ ? this.options.filterInputBlur
+ : true;
// If no input is passed create a default one
self = this;
// If selector was passed
@@ -246,6 +239,7 @@
this.fullData = this.options.data;
currentIndex = -1;
this.parseData(this.options.data);
+ this.focusTextInput();
} else {
this.remote = new GitLabDropdownRemote(this.options.data, {
dataType: this.options.dataType,
@@ -254,8 +248,9 @@
return function(data) {
_this.fullData = data;
_this.parseData(_this.fullData);
- if (_this.options.filterable && _this.filter && _this.filter.input) {
- return _this.filter.input.trigger('keyup');
+ _this.focusTextInput();
+ if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val().trim() !== '') {
+ return _this.filter.input.trigger('input');
}
};
// Remote data
@@ -430,7 +425,9 @@
var $target;
if (this.options.multiSelect) {
$target = $(e.target);
- if ($target && !$target.hasClass('dropdown-menu-close') && !$target.hasClass('dropdown-menu-close-icon') && !$target.data('is-link')) {
+ if ($target && !$target.hasClass('dropdown-menu-close') &&
+ !$target.hasClass('dropdown-menu-close-icon') &&
+ !$target.data('is-link')) {
e.stopPropagation();
return false;
} else {
@@ -443,6 +440,7 @@
var contentHtml;
this.resetRows();
this.addArrowKeyEvent();
+
if (this.options.setIndeterminateIds) {
this.options.setIndeterminateIds.call(this);
}
@@ -456,13 +454,24 @@
contentHtml = $('.dropdown-content', this.dropdown).html();
if (this.remote && contentHtml === "") {
this.remote.execute();
+ } else {
+ this.focusTextInput();
}
- if (this.options.filterable) {
- this.filterInput.focus();
+
+ if (this.options.showMenuAbove) {
+ this.positionMenuAbove();
}
+
return this.dropdown.trigger('shown.gl.dropdown');
};
+ GitLabDropdown.prototype.positionMenuAbove = function() {
+ var $button = $(this.el);
+ var $menu = this.dropdown.find('.dropdown-menu');
+
+ $menu.css('top', ($button.height() + $menu.height()) * -1);
+ };
+
GitLabDropdown.prototype.hidden = function(e) {
var $input;
this.resetRows();
@@ -474,7 +483,7 @@
// Triggering 'keyup' will re-render the dropdown which is not always required
// specially if we want to keep the state of the dropdown needed for bulk-assignment
if (!this.options.persistWhenHide) {
- $input.trigger("keyup");
+ $input.trigger("input");
}
if (this.dropdown.find(".dropdown-toggle-page").length) {
$('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS);
@@ -487,14 +496,27 @@
// Render the full menu
GitLabDropdown.prototype.renderMenu = function(html) {
- var menu_html;
- menu_html = "";
if (this.options.renderMenu) {
- menu_html = this.options.renderMenu(html);
+ return this.options.renderMenu(html);
} else {
- menu_html = $('<ul />').append(html);
+ var ul = document.createElement('ul');
+
+ for (var i = 0; i < html.length; i++) {
+ var el = html[i];
+
+ if (el instanceof jQuery) {
+ el = el.get(0);
+ }
+
+ if (typeof el === 'string') {
+ ul.innerHTML += el;
+ } else {
+ ul.appendChild(el);
+ }
+ }
+
+ return ul;
}
- return menu_html;
};
// Append the menu into the dropdown
@@ -508,7 +530,7 @@
};
GitLabDropdown.prototype.renderItem = function(data, group, index) {
- var cssClass, field, fieldName, groupAttrs, html, selected, text, url, value;
+ var field, fieldName, html, selected, text, url, value;
if (group == null) {
group = false;
}
@@ -516,18 +538,16 @@
// Render the row
index = false;
}
- html = "";
- // Divider
- if (data === "divider") {
- return "<li class='divider'></li>";
- }
- // Separator is a full-width divider
- if (data === "separator") {
- return "<li class='separator'></li>";
+ html = document.createElement('li');
+ if (data === 'divider' || data === 'separator') {
+ html.className = data;
+ return html;
}
// Header
if (data.header != null) {
- return _.template('<li class="dropdown-header"><%- header %></li>')({ header: data.header });
+ html.className = 'dropdown-header';
+ html.innerHTML = data.header;
+ return html;
}
if (this.options.renderRow) {
// Call the render function
@@ -537,6 +557,8 @@
value = this.options.id ? this.options.id(data) : data.id;
fieldName = this.options.fieldName;
+ if (value) { value = value.toString().replace(/'/g, '\\\'') };
+
field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']");
if (field.length) {
selected = true;
@@ -554,24 +576,25 @@
} else {
text = data.text != null ? data.text : '';
}
- cssClass = "";
- if (selected) {
- cssClass = "is-active";
- }
if (this.highlight) {
text = this.highlightTextMatches(text, this.filterInput.val());
}
+ // Create the list item & the link
+ var link = document.createElement('a');
+
+ link.href = url;
+ link.innerHTML = text;
+
+ if (selected) {
+ link.className = 'is-active';
+ }
+
if (group) {
- groupAttrs = 'data-group=' + group + ' data-index=' + index;
- } else {
- groupAttrs = '';
+ link.dataset.group = group;
+ link.dataset.index = index;
}
- html = _.template('<li><a href="<%- url %>" <%- groupAttrs %> class="<%- cssClass %>"><%= text %></a></li>')({
- url: url,
- groupAttrs: groupAttrs,
- cssClass: cssClass,
- text: text
- });
+
+ html.appendChild(link);
}
return html;
};
@@ -607,26 +630,42 @@
selectedObject = this.renderedData[selectedIndex];
}
}
- value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id;
+
+ if (this.options.vue) {
+ if (el.hasClass(ACTIVE_CLASS)) {
+ el.removeClass(ACTIVE_CLASS);
+ } else {
+ el.addClass(ACTIVE_CLASS);
+ }
+
+ return selectedObject;
+ }
+
+ field = [];
+ value = this.options.id
+ ? this.options.id(selectedObject, el)
+ : selectedObject.id;
if (isInput) {
field = $(this.el);
- } else {
- field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + escape(value) + "']");
+ } else if(value) {
+ field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']");
}
if (el.hasClass(ACTIVE_CLASS)) {
el.removeClass(ACTIVE_CLASS);
- if (isInput) {
- field.val('');
- } else {
- field.remove();
+ if (field && field.length) {
+ if (isInput) {
+ field.val('');
+ } else {
+ field.remove();
+ }
}
} else if (el.hasClass(INDETERMINATE_CLASS)) {
el.addClass(ACTIVE_CLASS);
el.removeClass(INDETERMINATE_CLASS);
- if (value == null) {
+ if (field && field.length && value == null) {
field.remove();
}
- if (!field.length && fieldName) {
+ if ((!field || !field.length) && fieldName) {
this.addInput(fieldName, value, selectedObject);
}
} else {
@@ -636,15 +675,15 @@
this.dropdown.parent().find("input[name='" + fieldName + "']").remove();
}
}
- if (value == null) {
+ if (field && field.length && value == null) {
field.remove();
}
// Toggle active class for the tick mark
el.addClass(ACTIVE_CLASS);
if (value != null) {
- if (!field.length && fieldName) {
+ if ((!field || !field.length) && fieldName) {
this.addInput(fieldName, value, selectedObject);
- } else {
+ } else if (field && field.length) {
field.val(value).trigger('change');
}
}
@@ -653,6 +692,10 @@
return selectedObject;
};
+ GitLabDropdown.prototype.focusTextInput = function() {
+ if (this.options.filterable) { this.filterInput.focus() }
+ }
+
GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) {
var $input;
// Create hidden input for form
@@ -722,6 +765,7 @@
return false;
}
if (currentKeyCode === 13 && currentIndex !== -1) {
+ e.preventDefault();
_this.selectRowAtIndex();
}
};
diff --git a/app/assets/javascripts/gl_field_error.js.es6 b/app/assets/javascripts/gl_field_error.js.es6
new file mode 100644
index 00000000000..f7cbecc0385
--- /dev/null
+++ b/app/assets/javascripts/gl_field_error.js.es6
@@ -0,0 +1,164 @@
+/* eslint-disable no-param-reassign */
+((global) => {
+ /*
+ * This class overrides the browser's validation error bubbles, displaying custom
+ * error messages for invalid fields instead. To begin validating any form, add the
+ * class `gl-show-field-errors` to the form element, and ensure error messages are
+ * declared in each inputs' `title` attribute. If no title is declared for an invalid
+ * field the user attempts to submit, "This field is required." will be shown by default.
+ *
+ * Opt not to validate certain fields by adding the class `gl-field-error-ignore` to the input.
+ *
+ * Set a custom error anchor for error message to be injected after with the
+ * class `gl-field-error-anchor`
+ *
+ * Examples:
+ *
+ * Basic:
+ *
+ * <form class='gl-show-field-errors'>
+ * <input type='text' name='username' title='Username is required.'/>
+ * </form>
+ *
+ * Ignore specific inputs (e.g. UsernameValidator):
+ *
+ * <form class='gl-show-field-errors'>
+ * <div class="form-group>
+ * <input type='text' class='gl-field-errors-ignore' pattern='[a-zA-Z0-9-_]+'/>
+ * </div>
+ * <div class="form-group">
+ * <input type='text' name='username' title='Username is required.'/>
+ * </div>
+ * </form>
+ *
+ * Custom Error Anchor (allows error message to be injected after specified element):
+ *
+ * <form class='gl-show-field-errors'>
+ * <div class="form-group gl-field-error-anchor">
+ * <input type='text' name='username' title='Username is required.'/>
+ * // Error message typically injected here
+ * </div>
+ * // Error message now injected here
+ * </form>
+ *
+ * */
+
+ /*
+ * Regex Patterns in use:
+ *
+ * Only alphanumeric: : "[a-zA-Z0-9]+"
+ * No special characters : "[a-zA-Z0-9-_]+",
+ *
+ * */
+
+ const errorMessageClass = 'gl-field-error';
+ const inputErrorClass = 'gl-field-error-outline';
+ const errorAnchorSelector = '.gl-field-error-anchor';
+ const ignoreInputSelector = '.gl-field-error-ignore';
+
+ class GlFieldError {
+ constructor({ input, formErrors }) {
+ this.inputElement = $(input);
+ this.inputDomElement = this.inputElement.get(0);
+ this.form = formErrors;
+ this.errorMessage = this.inputElement.attr('title') || 'This field is required.';
+ this.fieldErrorElement = $(`<p class='${errorMessageClass} hide'>${this.errorMessage}</p>`);
+
+ this.state = {
+ valid: false,
+ empty: true,
+ };
+
+ this.initFieldValidation();
+ }
+
+ initFieldValidation() {
+ const customErrorAnchor = this.inputElement.parents(errorAnchorSelector);
+ const errorAnchor = customErrorAnchor.length ? customErrorAnchor : this.inputElement;
+
+ // hidden when injected into DOM
+ errorAnchor.after(this.fieldErrorElement);
+ this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this));
+ this.scopedSiblings = this.safelySelectSiblings();
+ }
+
+ safelySelectSiblings() {
+ // Apply `ignoreSelector` in markup to siblings whose visibility should not be toggled
+ const unignoredSiblings = this.inputElement.siblings(`p:not(${ignoreInputSelector})`);
+ const parentContainer = this.inputElement.parent('.form-group');
+
+ // Only select siblings when they're scoped within a form-group with one input
+ const safelyScoped = parentContainer.length && parentContainer.find('input').length === 1;
+
+ return safelyScoped ? unignoredSiblings : this.fieldErrorElement;
+ }
+
+ renderValidity() {
+ this.renderClear();
+
+ if (this.state.valid) {
+ this.renderValid();
+ } else if (this.state.empty) {
+ this.renderEmpty();
+ } else if (!this.state.valid) {
+ this.renderInvalid();
+ }
+ }
+
+ handleInvalidSubmit(event) {
+ event.preventDefault();
+ const currentValue = this.accessCurrentValue();
+ this.state.valid = false;
+ this.state.empty = currentValue === '';
+
+ this.renderValidity();
+ this.form.focusOnFirstInvalid.apply(this.form);
+ // For UX, wait til after first invalid submission to check each keyup
+ this.inputElement.off('keyup.fieldValidator')
+ .on('keyup.fieldValidator', this.updateValidity.bind(this));
+ }
+
+ /* Get or set current input value */
+ accessCurrentValue(newVal) {
+ return newVal ? this.inputElement.val(newVal) : this.inputElement.val();
+ }
+
+ getInputValidity() {
+ return this.inputDomElement.validity.valid;
+ }
+
+ updateValidity() {
+ const inputVal = this.accessCurrentValue();
+ this.state.empty = !inputVal.length;
+ this.state.valid = this.getInputValidity();
+ this.renderValidity();
+ }
+
+ renderValid() {
+ return this.renderClear();
+ }
+
+ renderEmpty() {
+ return this.renderInvalid();
+ }
+
+ renderInvalid() {
+ this.inputElement.addClass(inputErrorClass);
+ this.scopedSiblings.hide();
+ return this.fieldErrorElement.show();
+ }
+
+ renderClear() {
+ const inputVal = this.accessCurrentValue();
+ if (!inputVal.split(' ').length) {
+ const trimmedInput = inputVal.trim();
+ this.accessCurrentValue(trimmedInput);
+ }
+ this.inputElement.removeClass(inputErrorClass);
+ this.scopedSiblings.hide();
+ this.fieldErrorElement.hide();
+ }
+ }
+
+ global.GlFieldError = GlFieldError;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/gl_field_errors.js.es6 b/app/assets/javascripts/gl_field_errors.js.es6
new file mode 100644
index 00000000000..6ce392d2a5b
--- /dev/null
+++ b/app/assets/javascripts/gl_field_errors.js.es6
@@ -0,0 +1,49 @@
+/* eslint-disable */
+
+//= require gl_field_error
+
+((global) => {
+ const customValidationFlag = 'gl-field-error-ignore';
+
+ class GlFieldErrors {
+ constructor(form) {
+ this.form = $(form);
+ this.state = {
+ inputs: [],
+ valid: false
+ };
+ this.initValidators();
+ }
+
+ initValidators () {
+ // register selectors here as needed
+ const validateSelectors = [':text', ':password', '[type=email]']
+ .map((selector) => `input${selector}`).join(',');
+
+ this.state.inputs = this.form.find(validateSelectors).toArray()
+ .filter((input) => !input.classList.contains(customValidationFlag))
+ .map((input) => new global.GlFieldError({ input, formErrors: this }));
+
+ this.form.on('submit', this.catchInvalidFormSubmit);
+ }
+
+ /* Neccessary 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 */
+
+ catchInvalidFormSubmit (event) {
+ if (!event.currentTarget.checkValidity()) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ focusOnFirstInvalid () {
+ const firstInvalid = this.state.inputs.filter((input) => !input.inputDomElement.validity.valid)[0];
+ firstInvalid.inputElement.focus();
+ }
+ }
+
+ global.GlFieldErrors = GlFieldErrors;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index 2703adc0705..db5d9e75b3a 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-undef, no-new, padded-blocks, max-len */
(function() {
this.GLForm = (function() {
function GLForm(form) {
@@ -23,8 +24,8 @@
if (isNewForm) {
this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form');
- disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
// remove notify commit author checkbox for non-commit notes
+ gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new DropzoneInput(this.form);
autosize(this.textarea);
diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js
index 4886da9f21f..32c26349da0 100644
--- a/app/assets/javascripts/graphs/graphs_bundle.js
+++ b/app/assets/javascripts/graphs/graphs_bundle.js
@@ -1,5 +1,6 @@
+/* eslint-disable func-names, space-before-function-paren */
// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
diff --git a/app/assets/javascripts/graphs/stat_graph.js b/app/assets/javascripts/graphs/stat_graph.js
index f041980bc19..3273bf3a263 100644
--- a/app/assets/javascripts/graphs/stat_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-return-assign, padded-blocks, max-len */
(function() {
this.StatGraph = (function() {
function StatGraph() {}
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js
index 927d241b357..c3a132b3c75 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, no-undef, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, padded-blocks, max-len */
/*= require d3 */
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
index 7d9d4d7c679..cb2448e8cc7 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, one-var, no-var, space-before-blocks, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, 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, padded-blocks, no-undef, newline-per-chained-call, no-else-return, max-len */
/*= require d3 */
@@ -29,8 +30,7 @@
ContributorsGraph.set_y_domain = function(data) {
return ContributorsGraph.prototype.y_domain = [
0, d3.max(data, function(d) {
- var ref, ref1;
- return d.commits = (ref = (ref1 = d.commits) != null ? ref1 : d.additions) != null ? ref : d.deletions;
+ return d.commits = d.commits || d.additions || d.deletions;
})
];
};
@@ -44,8 +44,7 @@
ContributorsGraph.init_y_domain = function(data) {
return ContributorsGraph.prototype.y_domain = [
0, d3.max(data, function(d) {
- var ref, ref1;
- return d.commits = (ref = (ref1 = d.commits) != null ? ref1 : d.additions) != null ? ref : d.deletions;
+ return d.commits = d.commits || d.additions || d.deletions;
})
];
};
@@ -147,9 +146,8 @@
return this.area = d3.svg.area().x(function(d) {
return x(d.date);
}).y0(this.height).y1(function(d) {
- var ref, ref1, xa;
- xa = d.commits = (ref = (ref1 = d.commits) != null ? ref1 : d.additions) != null ? ref : d.deletions;
- return y(xa);
+ d.commits = d.commits || d.additions || d.deletions;
+ return y(d.commits);
}).interpolate("basis");
};
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/graphs/stat_graph_contributors_util.js
index 0d240bed8b6..051ff98c774 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_util.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, no-plusplus, 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, padded-blocks, max-len */
(function() {
window.ContributorsStatGraphUtil = {
parse_log: function(log) {
diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js
index c28ce86d7af..17a76168a79 100644
--- a/app/assets/javascripts/group_avatar.js
+++ b/app/assets/javascripts/group_avatar.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, one-var, one-var-declaration-per-line, no-useless-escape, padded-blocks, max-len */
(function() {
this.GroupAvatar = (function() {
function GroupAvatar() {
diff --git a/app/assets/javascripts/group_label_subscription.js.es6 b/app/assets/javascripts/group_label_subscription.js.es6
new file mode 100644
index 00000000000..eea6cd40859
--- /dev/null
+++ b/app/assets/javascripts/group_label_subscription.js.es6
@@ -0,0 +1,53 @@
+/* eslint-disable */
+(function(global) {
+ class GroupLabelSubscription {
+ constructor(container) {
+ const $container = $(container);
+ this.$dropdown = $container.find('.dropdown');
+ this.$subscribeButtons = $container.find('.js-subscribe-button');
+ this.$unsubscribeButtons = $container.find('.js-unsubscribe-button');
+
+ this.$subscribeButtons.on('click', this.subscribe.bind(this));
+ this.$unsubscribeButtons.on('click', this.unsubscribe.bind(this));
+ }
+
+ unsubscribe(event) {
+ event.preventDefault();
+
+ const url = this.$unsubscribeButtons.attr('data-url');
+
+ $.ajax({
+ type: 'POST',
+ url: url
+ }).done(() => {
+ this.toggleSubscriptionButtons();
+ this.$unsubscribeButtons.removeAttr('data-url');
+ });
+ }
+
+ subscribe(event) {
+ event.preventDefault();
+
+ const $btn = $(event.currentTarget);
+ const url = $btn.attr('data-url');
+
+ this.$unsubscribeButtons.attr('data-url', url);
+
+ $.ajax({
+ type: 'POST',
+ url: url
+ }).done(() => {
+ this.toggleSubscriptionButtons();
+ });
+ }
+
+ toggleSubscriptionButtons() {
+ this.$dropdown.toggleClass('hidden');
+ this.$subscribeButtons.toggleClass('hidden');
+ this.$unsubscribeButtons.toggleClass('hidden');
+ }
+ }
+
+ global.GroupLabelSubscription = GroupLabelSubscription;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/groups.js b/app/assets/javascripts/groups.js
deleted file mode 100644
index 4382dd6860f..00000000000
--- a/app/assets/javascripts/groups.js
+++ /dev/null
@@ -1,13 +0,0 @@
-(function() {
- this.GroupMembers = (function() {
- function GroupMembers() {
- $('li.group_member').bind('ajax:success', function() {
- return $(this).fadeOut();
- });
- }
-
- return GroupMembers;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index 7c2eebcdd44..3dc6f05ca20 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, one-var, camelcase, one-var-declaration-per-line, quotes, object-shorthand, no-undef, prefer-arrow-callback, comma-dangle, consistent-return, yoda, prefer-rest-params, prefer-spread, no-unused-vars, prefer-template, padded-blocks, max-len */
(function() {
var slice = [].slice;
@@ -5,14 +6,16 @@
function GroupsSelect() {
$('.ajax-groups-select').each((function(_this) {
return function(i, select) {
- var skip_ldap;
- skip_ldap = $(select).hasClass('skip_ldap');
+ var all_available, skip_groups;
+ all_available = $(select).data('all-available');
+ skip_groups = $(select).data('skip-groups') || [];
return $(select).select2({
placeholder: "Search for a group",
multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0,
query: function(query) {
- return Api.groups(query.term, skip_ldap, function(groups) {
+ options = { all_available: all_available, skip_groups: skip_groups };
+ return Api.groups(query.term, options, function(groups) {
var data;
data = {
results: groups
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
new file mode 100644
index 00000000000..c7cbf9ca44b
--- /dev/null
+++ b/app/assets/javascripts/header.js
@@ -0,0 +1,10 @@
+/* eslint-disable wrap-iife, func-names, space-before-function-paren, padded-blocks, prefer-arrow-callback, no-var, max-len */
+(function() {
+
+ $(document).on('todo:toggle', function(e, count) {
+ var $todoPendingCount = $('.todos-pending-count');
+ $todoPendingCount.text(gl.text.addDelimiter(count));
+ $todoPendingCount.toggleClass('hidden', count === 0);
+ });
+
+})();
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
index 9efad1ce943..9425b6ed9d4 100644
--- a/app/assets/javascripts/importer_status.js
+++ b/app/assets/javascripts/importer_status.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, camelcase, no-var, one-var, one-var-declaration-per-line, prefer-template, quotes, object-shorthand, comma-dangle, no-unused-vars, prefer-arrow-callback, no-else-return, padded-blocks, vars-on-top, no-new, no-undef, max-len */
(function() {
this.ImporterStatus = (function() {
function ImporterStatus(jobs_url, import_url) {
@@ -10,24 +11,24 @@
ImporterStatus.prototype.initStatusPage = function() {
$('.js-add-to-import').off('click').on('click', (function(_this) {
return function(e) {
- var $btn, $namespace_input, $target_field, $tr, id, target_namespace;
+ var $btn, $namespace_input, $target_field, $tr, id, target_namespace, newName;
$btn = $(e.currentTarget);
$tr = $btn.closest('tr');
$target_field = $tr.find('.import-target');
- $namespace_input = $target_field.find('input');
+ $namespace_input = $target_field.find('.js-select-namespace option:selected');
id = $tr.attr('id').replace('repo_', '');
target_namespace = null;
-
+ newName = null;
if ($namespace_input.length > 0) {
- target_namespace = $namespace_input.prop('value');
- $target_field.empty().append(target_namespace + "/" + ($target_field.data('project_name')));
+ target_namespace = $namespace_input[0].innerHTML;
+ newName = $target_field.find('#path').prop('value');
+ $target_field.empty().append(target_namespace + "/" + newName);
}
-
$btn.disable().addClass('is-loading');
-
return $.post(_this.import_url, {
repo_id: id,
- target_namespace: target_namespace
+ target_namespace: target_namespace,
+ new_name: newName
}, {
dataType: 'script'
});
diff --git a/app/assets/javascripts/issuable.js.es6 b/app/assets/javascripts/issuable.js.es6
index 53faaa38a0c..46503c290ae 100644
--- a/app/assets/javascripts/issuable.js.es6
+++ b/app/assets/javascripts/issuable.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(function() {
var issuable_created;
@@ -9,30 +10,88 @@
Issuable.initSearch();
Issuable.initChecks();
Issuable.initResetFilters();
+ Issuable.resetIncomingEmailToken();
return Issuable.initLabelFilterRemove();
},
initTemplates: function() {
return Issuable.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>');
},
initSearch: function() {
- this.timer = null;
- return $('#issue_search').off('keyup').on('keyup', function() {
- clearTimeout(this.timer);
- return this.timer = setTimeout(function() {
- var $form, $input, $search;
- $search = $('#issue_search');
- $form = $('.js-filter-form');
- $input = $("input[name='" + ($search.attr('name')) + "']", $form);
- if ($input.length === 0) {
- $form.append("<input type='hidden' name='" + ($search.attr('name')) + "' value='" + (_.escape($search.val())) + "'/>");
- } else {
- $input.val($search.val());
- }
- if ($search.val() !== '') {
- return Issuable.filterResults($form);
- }
- }, 500);
+ const $searchInput = $('#issuable_search');
+
+ Issuable.initSearchState($searchInput);
+
+ // `immediate` param set to false debounces on the `trailing` edge, lets user finish typing
+ const debouncedExecSearch = _.debounce(Issuable.executeSearch, 1000, false);
+
+ $searchInput.off('keyup').on('keyup', debouncedExecSearch);
+
+ // ensures existing filters are preserved when manually submitted
+ $('#issuable_search_form').on('submit', (e) => {
+ e.preventDefault();
+ debouncedExecSearch(e);
});
+
+ },
+ initSearchState: function($searchInput) {
+ const currentSearchVal = $searchInput.val();
+
+ Issuable.searchState = {
+ elem: $searchInput,
+ current: currentSearchVal
+ };
+
+ Issuable.maybeFocusOnSearch();
+ },
+ accessSearchPristine: function(set) {
+ // store reference to previous value to prevent search on non-mutating keyup
+ const state = Issuable.searchState;
+ const currentSearchVal = state.elem.val();
+
+ if (set) {
+ state.current = currentSearchVal;
+ } else {
+ return state.current === currentSearchVal;
+ }
+ },
+ maybeFocusOnSearch: function() {
+ const currentSearchVal = Issuable.searchState.current;
+ if (currentSearchVal && currentSearchVal !== '') {
+ const queryLength = currentSearchVal.length;
+ const $searchInput = Issuable.searchState.elem;
+
+ /* The following ensures that the cursor is initially placed at
+ * the end of search input when focus is applied. It accounts
+ * for differences in browser implementations of `setSelectionRange`
+ * and cursor placement for elements in focus.
+ */
+ $searchInput.focus();
+ if ($searchInput.setSelectionRange) {
+ $searchInput.setSelectionRange(queryLength, queryLength);
+ } else {
+ $searchInput.val(currentSearchVal);
+ }
+ }
+ },
+ executeSearch: function(e) {
+ const $search = $('#issuable_search');
+ const $searchName = $search.attr('name');
+ const $searchValue = $search.val();
+ const $filtersForm = $('.js-filter-form');
+ const $input = $(`input[name='${$searchName}']`, $filtersForm);
+ const isPristine = Issuable.accessSearchPristine();
+
+ if (isPristine) {
+ return;
+ }
+
+ if (!$input.length) {
+ $filtersForm.append(`<input type='hidden' name='${$searchName}' value='${_.escape($searchValue)}'/>`);
+ } else {
+ $input.val($searchValue);
+ }
+
+ Issuable.filterResults($filtersForm);
},
initLabelFilterRemove: function() {
return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) {
@@ -44,7 +103,6 @@
}).remove();
// Submit the form to get new data
Issuable.filterResults($('.filter-form'));
- return $('.js-label-select').trigger('update.label');
});
},
filterResults: (function(_this) {
@@ -97,6 +155,27 @@
this.issuableBulkActions.willUpdateLabels = false;
}
return true;
+ },
+
+ resetIncomingEmailToken: function() {
+ $('.incoming-email-token-reset').on('click', function(e) {
+ e.preventDefault();
+
+ $.ajax({
+ type: 'PUT',
+ url: $('.incoming-email-token-reset').attr('href'),
+ dataType: 'json',
+ success: function(response) {
+ $('#issue_email').val(response.new_issue_address).focus();
+ },
+ beforeSend: function() {
+ $('.incoming-email-token-reset').text('resetting...');
+ },
+ complete: function() {
+ $('.incoming-email-token-reset').text('reset it');
+ }
+ });
+ });
}
};
diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js
index 8147e83ffe8..317818951fd 100644
--- a/app/assets/javascripts/issuable_context.js
+++ b/app/assets/javascripts/issuable_context.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, no-undef, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, padded-blocks, max-len */
(function() {
this.IssuableContext = (function() {
function IssuableContext(currentUser) {
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index b7f92ae9883..50fdbc89c7c 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-undef, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, radix, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -44,8 +45,8 @@
};
IssuableForm.prototype.handleSubmit = function() {
- var ref, ref1;
- if (((ref = parseInt((ref1 = this.issueMoveField) != null ? ref1.val() : void 0)) != null ? ref : 0) > 0) {
+ var fieldId = (this.issueMoveField != null) ? this.issueMoveField.val() : null;
+ if ((parseInt(fieldId) || 0) > 0) {
if (!confirm(this.issueMoveConfirmMsg)) {
return false;
}
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 261bf6137c2..8540b199aba 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-undef, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, padded-blocks, max-len */
/*= require flash */
/*= require jquery.waitforimages */
@@ -94,7 +95,11 @@
return $.ajax({
type: 'PATCH',
url: $('form.js-issuable-update').attr('action'),
- data: patchData
+ data: patchData,
+ success: function(issue) {
+ document.querySelector('#task_status').innerText = issue.task_status;
+ document.querySelector('#task_status_short').innerText = issue.task_status_short;
+ }
});
// TODO (rspeicher): Make the issue description inline-editable like a note so
// that we can re-use its form here
diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js
index 076e3972944..b39d8274e13 100644
--- a/app/assets/javascripts/issue_status_select.js
+++ b/app/assets/javascripts/issue_status_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */
(function() {
this.IssueStatusSelect = (function() {
function IssueStatusSelect() {
diff --git a/app/assets/javascripts/issues-bulk-assignment.js b/app/assets/javascripts/issues_bulk_assignment.js.es6
index 62a7fc9a06c..9697fb33566 100644
--- a/app/assets/javascripts/issues-bulk-assignment.js
+++ b/app/assets/javascripts/issues_bulk_assignment.js.es6
@@ -1,13 +1,11 @@
-(function() {
- this.IssuableBulkActions = (function() {
- function IssuableBulkActions(opts) {
- // Set defaults
- var ref, ref1, ref2;
- if (opts == null) {
- opts = {};
- }
- this.container = (ref = opts.container) != null ? ref : $('.content'), this.form = (ref1 = opts.form) != null ? ref1 : this.getElement('.bulk-update'), this.issues = (ref2 = opts.issues) != null ? ref2 : this.getElement('.issuable-list > li');
- // Save instance
+/* eslint-disable */
+((global) => {
+
+ class IssuableBulkActions {
+ constructor({ container, form, issues } = {}) {
+ this.container = container || $('.content'),
+ this.form = form || this.getElement('.bulk-update');
+ this.issues = issues || this.getElement('.issues-list .issue');
this.form.data('bulkActions', this);
this.willUpdateLabels = false;
this.bindEvents();
@@ -15,53 +13,46 @@
Issuable.initChecks();
}
- IssuableBulkActions.prototype.getElement = function(selector) {
+ getElement(selector) {
return this.container.find(selector);
- };
+ }
- IssuableBulkActions.prototype.bindEvents = function() {
+ bindEvents() {
return this.form.off('submit').on('submit', this.onFormSubmit.bind(this));
- };
+ }
- IssuableBulkActions.prototype.onFormSubmit = function(e) {
+ onFormSubmit(e) {
e.preventDefault();
return this.submit();
- };
+ }
- IssuableBulkActions.prototype.submit = function() {
- var _this, xhr;
- _this = this;
- xhr = $.ajax({
+ submit() {
+ const _this = this;
+ const xhr = $.ajax({
url: this.form.attr('action'),
method: this.form.attr('method'),
dataType: 'JSON',
data: this.getFormDataAsObject()
});
- xhr.done(function(response, status, xhr) {
- return location.reload();
- });
- xhr.fail(function() {
- return new Flash("Issue update failed");
- });
+ xhr.done(() => window.location.reload());
+ xhr.fail(() => new Flash("Issue update failed"));
return xhr.always(this.onFormSubmitAlways.bind(this));
- };
+ }
- IssuableBulkActions.prototype.onFormSubmitAlways = function() {
+ onFormSubmitAlways() {
return this.form.find('[type="submit"]').enable();
- };
+ }
- IssuableBulkActions.prototype.getSelectedIssues = function() {
+ getSelectedIssues() {
return this.issues.has('.selected_issue:checked');
- };
+ }
- IssuableBulkActions.prototype.getLabelsFromSelection = function() {
- var labels;
- labels = [];
+ getLabelsFromSelection() {
+ const labels = [];
this.getSelectedIssues().map(function() {
- var _labels;
- _labels = $(this).data('labels');
- if (_labels) {
- return _labels.map(function(labelId) {
+ const labelsData = $(this).data('labels');
+ if (labelsData) {
+ return labelsData.map(function(labelId) {
if (labels.indexOf(labelId) === -1) {
return labels.push(labelId);
}
@@ -69,7 +60,7 @@
}
});
return labels;
- };
+ }
/**
@@ -77,25 +68,21 @@
* @return {Array} Label IDs
*/
- IssuableBulkActions.prototype.getUnmarkedIndeterminedLabels = function() {
- var el, i, id, j, labelsToKeep, len, len1, ref, ref1, result;
- result = [];
- labelsToKeep = [];
- ref = this.getElement('.labels-filter .is-indeterminate');
- for (i = 0, len = ref.length; i < len; i++) {
- el = ref[i];
- labelsToKeep.push($(el).data('labelId'));
- }
- ref1 = this.getLabelsFromSelection();
- for (j = 0, len1 = ref1.length; j < len1; j++) {
- id = ref1[j];
- // Only the ones that we are not going to keep
+ getUnmarkedIndeterminedLabels() {
+ const result = [];
+ const labelsToKeep = [];
+
+ this.getElement('.labels-filter .is-indeterminate')
+ .each((i, el) => labelsToKeep.push($(el).data('labelId')));
+
+ this.getLabelsFromSelection().forEach((id) => {
if (labelsToKeep.indexOf(id) === -1) {
result.push(id);
}
- }
+ });
+
return result;
- };
+ }
/**
@@ -103,9 +90,8 @@
* Returns key/value pairs from form data
*/
- IssuableBulkActions.prototype.getFormDataAsObject = function() {
- var formData;
- formData = {
+ getFormDataAsObject() {
+ const formData = {
update: {
state_event: this.form.find('input[name="update[state_event]"]').val(),
assignee_id: this.form.find('input[name="update[assignee_id]"]').val(),
@@ -125,19 +111,18 @@
});
}
return formData;
- };
+ }
- IssuableBulkActions.prototype.getLabelsToApply = function() {
- var $labels, labelIds;
- labelIds = [];
- $labels = this.form.find('.labels-filter input[name="update[label_ids][]"]');
+ getLabelsToApply() {
+ const labelIds = [];
+ const $labels = this.form.find('.labels-filter input[name="update[label_ids][]"]');
$labels.each(function(k, label) {
if (label) {
return labelIds.push(parseInt($(label).val()));
}
});
return labelIds;
- };
+ }
/**
@@ -145,11 +130,10 @@
* @return {Array} Array of labels IDs
*/
- IssuableBulkActions.prototype.getLabelsToRemove = function() {
- var indeterminatedLabels, labelsToApply, result;
- result = [];
- indeterminatedLabels = this.getUnmarkedIndeterminedLabels();
- labelsToApply = this.getLabelsToApply();
+ getLabelsToRemove() {
+ const result = [];
+ const indeterminatedLabels = this.getUnmarkedIndeterminedLabels();
+ const labelsToApply = this.getLabelsToApply();
indeterminatedLabels.map(function(id) {
// We need to exclude label IDs that will be applied
// By not doing this will cause issues from selection to not add labels at all
@@ -158,10 +142,9 @@
}
});
return result;
- };
-
- return IssuableBulkActions;
+ }
+ }
- })();
+ global.IssuableBulkActions = IssuableBulkActions;
-}).call(this);
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/label_manager.js.es6 b/app/assets/javascripts/label_manager.js.es6
new file mode 100644
index 00000000000..175623e7448
--- /dev/null
+++ b/app/assets/javascripts/label_manager.js.es6
@@ -0,0 +1,107 @@
+/* eslint-disable */
+((global) => {
+
+ class LabelManager {
+ constructor({ togglePriorityButton, prioritizedLabels, otherLabels } = {}) {
+ this.togglePriorityButton = togglePriorityButton || $('.js-toggle-priority');
+ this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels');
+ this.otherLabels = otherLabels || $('.js-other-labels');
+ this.errorMessage = 'Unable to update label prioritization at this time';
+ this.prioritizedLabels.sortable({
+ items: 'li',
+ placeholder: 'list-placeholder',
+ axis: 'y',
+ update: this.onPrioritySortUpdate.bind(this)
+ });
+ this.bindEvents();
+ }
+
+ bindEvents() {
+ return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick);
+ }
+
+ onTogglePriorityClick(e) {
+ e.preventDefault();
+ const _this = e.data;
+ const $btn = $(e.currentTarget);
+ const $label = $(`#${$btn.data('domId')}`);
+ const action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
+ const $tooltip = $(`#${$btn.find('.has-tooltip:visible').attr('aria-describedby')}`);
+ $tooltip.tooltip('destroy');
+ return _this.toggleLabelPriority($label, action);
+ }
+
+ toggleLabelPriority($label, action, persistState) {
+ if (persistState == null) {
+ persistState = true;
+ }
+ let xhr;
+ const _this = this;
+ const url = $label.find('.js-toggle-priority').data('url');
+ let $target = this.prioritizedLabels;
+ let $from = this.otherLabels;
+ if (action === 'remove') {
+ $target = this.otherLabels;
+ $from = this.prioritizedLabels;
+ }
+ if ($from.find('li').length === 1) {
+ $from.find('.empty-message').removeClass('hidden');
+ }
+ if (!$target.find('li').length) {
+ $target.find('.empty-message').addClass('hidden');
+ }
+ $label.detach().appendTo($target);
+ // Return if we are not persisting state
+ if (!persistState) {
+ return;
+ }
+ if (action === 'remove') {
+ xhr = $.ajax({
+ url,
+ type: 'DELETE'
+ });
+ // Restore empty message
+ if (!$from.find('li').length) {
+ $from.find('.empty-message').removeClass('hidden');
+ }
+ } else {
+ xhr = this.savePrioritySort($label, action);
+ }
+ return xhr.fail(this.rollbackLabelPosition.bind(this, $label, action));
+ }
+
+ onPrioritySortUpdate() {
+ const xhr = this.savePrioritySort();
+ return xhr.fail(function() {
+ return new Flash(this.errorMessage, 'alert');
+ });
+ }
+
+ savePrioritySort() {
+ return $.post({
+ url: this.prioritizedLabels.data('url'),
+ data: {
+ label_ids: this.getSortedLabelsIds()
+ }
+ });
+ }
+
+ rollbackLabelPosition($label, originalAction) {
+ const action = originalAction === 'remove' ? 'add' : 'remove';
+ this.toggleLabelPriority($label, action, false);
+ return new Flash(this.errorMessage, 'alert');
+ }
+
+ getSortedLabelsIds() {
+ const sortedIds = [];
+ this.prioritizedLabels.find('li').each(function() {
+ sortedIds.push($(this).data('id'));
+ });
+ return sortedIds;
+ }
+ }
+
+ gl.LabelManager = LabelManager;
+
+})(window.gl || (window.gl = {}));
+
diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js
index cb16e2ba814..10de13c9a8a 100644
--- a/app/assets/javascripts/labels.js
+++ b/app/assets/javascripts/labels.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 29a967a35a0..812d5cde685 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,12 +1,15 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, 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, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
(function() {
this.LabelsSelect = (function() {
function LabelsSelect() {
var _this;
_this = this;
$('.js-label-select').each(function(i, dropdown) {
- var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, projectId, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip;
+ var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove;
$dropdown = $(dropdown);
- projectId = $dropdown.data('project-id');
+ $toggleText = $dropdown.find('.dropdown-toggle-text');
+ namespacePath = $dropdown.data('namespace-path');
+ projectPath = $dropdown.data('project-path');
labelUrl = $dropdown.data('labels');
issueUpdateURL = $dropdown.data('issueUpdate');
selectedLabel = $dropdown.data('selected');
@@ -15,15 +18,24 @@
}
showNo = $dropdown.data('show-no');
showAny = $dropdown.data('show-any');
+ showMenuAbove = $dropdown.data('showMenuAbove');
defaultLabel = $dropdown.data('default-label');
abilityName = $dropdown.data('ability-name');
$selectbox = $dropdown.closest('.selectbox');
$block = $selectbox.closest('.block');
- $form = $dropdown.closest('form');
+ $form = $dropdown.closest('form, .js-issuable-update');
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span');
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
$value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut();
+ fieldName = $dropdown.data('field-name');
+ 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('field-name') + '"]')
+ .map(function () {
+ return this.value;
+ }).get();
if (issueUpdateURL != null) {
issueURLSplit = issueUpdateURL.split('/');
}
@@ -35,14 +47,18 @@
$sidebarLabelTooltip.tooltip();
if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) {
- new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), projectId);
+ new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), namespacePath, projectPath);
}
saveLabelData = function() {
var data, selected;
- selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").map(function() {
+ selected = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "']").map(function() {
return this.value;
}).get();
+
+ if (_.isEqual(initialSelected, selected)) return;
+ initialSelected = selected;
+
data = {};
data[abilityName] = {};
data[abilityName].label_ids = selected;
@@ -66,7 +82,8 @@
if (data.labels.length) {
template = labelHTMLTemplate(data);
labelCount = data.labels.length;
- } else {
+ }
+ else {
template = labelNoneHTMLTemplate;
}
$value.removeAttr('style').html(template);
@@ -83,7 +100,8 @@
}
labelTooltipTitle = labelTitles.join(', ');
- } else {
+ }
+ else {
labelTooltipTitle = '';
$sidebarLabelTooltip.tooltip('destroy');
}
@@ -105,6 +123,7 @@
});
};
return $dropdown.glDropdown({
+ showMenuAbove: showMenuAbove,
data: function(term, callback) {
return $.ajax({
url: labelUrl
@@ -124,23 +143,29 @@
};
}).value();
if ($dropdown.hasClass('js-extra-options')) {
+ var extraData = [];
if (showNo) {
- data.unshift({
+ extraData.unshift({
id: 0,
title: 'No Label'
});
}
if (showAny) {
- data.unshift({
+ extraData.unshift({
isAny: true,
title: 'Any Label'
});
}
- if (data.length > 2) {
- data.splice(2, 0, 'divider');
+ if (extraData.length) {
+ extraData.push('divider');
+ data = extraData.concat(data);
}
}
- return callback(data);
+
+ callback(data);
+ if (showMenuAbove) {
+ $dropdown.data('glDropdown').positionMenuAbove();
+ }
});
},
renderRow: function(label, instance) {
@@ -148,7 +173,7 @@
$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 = instance.indeterminateIds;
active = instance.activeIds;
@@ -166,7 +191,7 @@
instance.addInput(this.fieldName, label.id);
}
}
- if ($form.find("input[type='hidden'][name='" + ($dropdown.data('fieldName')) + "'][value='" + escape(this.id(label)) + "']").length) {
+ if (this.id(label) && $form.find("input[type='hidden'][name='" + ($dropdown.data('fieldName')) + "'][value='" + this.id(label).toString().replace(/'/g, '\\\'') + "']").length) {
selectedClass.push('is-active');
}
if ($dropdown.hasClass('js-multiselect') && removesAll) {
@@ -185,14 +210,16 @@
return color + " " + percentFirst + "%," + color + " " + percentSecond + "% ";
}).join(',');
color = "linear-gradient(" + color + ")";
- } else {
+ }
+ else {
if (label.color != null) {
color = label.color[0];
}
}
if (color) {
colorEl = "<span class='dropdown-label-box' style='background: " + color + "'></span>";
- } else {
+ }
+ else {
colorEl = '';
}
// We need to identify which items are actually labels
@@ -210,30 +237,46 @@
},
selectable: true,
filterable: true,
+ selected: $dropdown.data('selected') || [],
toggleLabel: function(selected, el) {
- var selected_labels;
- selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active');
- if (selected && (selected.title != null)) {
- if (selected_labels.length > 1) {
- return selected.title + " +" + (selected_labels.length - 1) + " more";
- } else {
- return selected.title;
- }
- } else if (!selected && selected_labels.length !== 0) {
- if (selected_labels.length > 1) {
- return ($(selected_labels[0]).text()) + " +" + (selected_labels.length - 1) + " more";
- } else if (selected_labels.length === 1) {
- return $(selected_labels).text();
- }
- } else {
+ var isSelected = el !== null ? el.hasClass('is-active') : false;
+ var title = selected.title;
+ var selectedLabels = this.selected;
+
+ if (selected.id === 0) {
+ this.selected = [];
+ return 'No Label';
+ }
+ else if (isSelected) {
+ this.selected.push(title);
+ }
+ else {
+ var index = this.selected.indexOf(title);
+ this.selected.splice(index, 1);
+ }
+
+ if (selectedLabels.length === 1) {
+ return selectedLabels;
+ }
+ else if (selectedLabels.length) {
+ return selectedLabels[0] + " +" + (selectedLabels.length - 1) + " more";
+ }
+ else {
return defaultLabel;
}
},
fieldName: $dropdown.data('field-name'),
id: function(label) {
+ if (label.id <= 0) return label.title;
+
+ if ($dropdown.hasClass('js-issuable-form-dropdown')) {
+ return label.id;
+ }
+
if ($dropdown.hasClass("js-filter-submit") && (label.isAny == null)) {
return label.title;
- } else {
+ }
+ else {
return label.id;
}
},
@@ -245,16 +288,23 @@
$selectbox.hide();
// display:block overrides the hide-collapse rule
$value.removeAttr('style');
- if (page === 'projects:boards:show') {
+
+ if ($dropdown.hasClass('js-issuable-form-dropdown')) {
+ return;
+ }
+
+ if ($('html').hasClass('issue-boards-page')) {
return;
}
if ($dropdown.hasClass('js-multiselect')) {
if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
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();
}
@@ -268,24 +318,35 @@
}
},
multiSelect: $dropdown.hasClass('js-multiselect'),
+ vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(label, $el, e) {
var isIssueIndex, isMRIndex, page;
_this.enableBulkLabelDropdown();
- if ($dropdown.hasClass('js-filter-bulk-update')) {
+
+ if ($dropdown.parent().find('.is-active:not(.dropdown-clear-active)').length) {
+ $dropdown.parent()
+ .find('.dropdown-clear-active')
+ .removeClass('is-active')
+ }
+
+ if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
return;
}
+
page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index';
isMRIndex = page === 'projects:merge_requests:index';
- if (page === 'projects:boards:show') {
+ if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar')) {
if (label.isAny) {
gl.issueBoards.BoardsStore.state.filters['label_name'] = [];
- } else if (label.title) {
+ }
+ else if ($el.hasClass('is-active')) {
gl.issueBoards.BoardsStore.state.filters['label_name'].push(label.title);
- } else {
+ }
+ else {
var filters = gl.issueBoards.BoardsStore.state.filters['label_name'];
- filters = filters.filter(function (label) {
- return label !== $el.text().trim();
+ filters = filters.filter(function (filteredLabel) {
+ return filteredLabel !== label.title;
});
gl.issueBoards.BoardsStore.state.filters['label_name'] = filters;
}
@@ -293,17 +354,45 @@
gl.issueBoards.BoardsStore.updateFiltersUrl();
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 {
+ }
+ 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 = labels.filter(function (selectedLabel) {
+ return selectedLabel.id !== label.id;
+ });
+ gl.issueBoards.BoardsStore.detail.issue.labels = labels;
+ }
+
+ $loading.fadeIn();
+
+ gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
+ .then(function () {
+ $loading.fadeOut();
+ });
+ }
+ else {
if ($dropdown.hasClass('js-multiselect')) {
- } else {
+ }
+ else {
return saveLabelData();
}
}
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index ce472f3bcd0..2b700539c2b 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, indent, vars-on-top, padded-blocks, max-len */
(function() {
var hideEndFade;
@@ -10,11 +11,13 @@
};
$(function() {
- hideEndFade($('.scrolling-tabs'));
+ var $scrollingTabs = $('.scrolling-tabs');
+
+ hideEndFade($scrollingTabs);
$(window).off('resize.nav').on('resize.nav', function() {
- return hideEndFade($('.scrolling-tabs'));
+ return hideEndFade($scrollingTabs);
});
- return $('.scrolling-tabs').on('scroll', function(event) {
+ $scrollingTabs.off('scroll').on('scroll', function(event) {
var $this, currentPosition, maxPosition;
$this = $(this);
currentPosition = $this.scrollLeft();
@@ -22,6 +25,23 @@
$this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0);
return $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1);
});
+
+ $scrollingTabs.each(function () {
+ var $this = $(this),
+ scrollingTabWidth = $this.width(),
+ $active = $this.find('.active'),
+ activeWidth = $active.width();
+
+ if ($active.length) {
+ var offset = $active.offset().left + activeWidth;
+
+ if (offset > scrollingTabWidth - 30) {
+ var scrollLeft = scrollingTabWidth / 2;
+ scrollLeft = (offset - scrollLeft) - (activeWidth / 2);
+ $this.scrollLeft(scrollLeft);
+ }
+ }
+ });
});
}).call(this);
diff --git a/app/assets/javascripts/lib/ace.js b/app/assets/javascripts/lib/ace.js
index 4cdf99cae72..b1718e89d3d 100644
--- a/app/assets/javascripts/lib/ace.js
+++ b/app/assets/javascripts/lib/ace.js
@@ -1,2 +1,3 @@
+/* eslint-disable */
/*= require ace-rails-ap */
/*= require ace/ext-searchbox */
diff --git a/app/assets/javascripts/lib/chart.js b/app/assets/javascripts/lib/chart.js
index d9b07c10a49..d8ad5aaeffe 100644
--- a/app/assets/javascripts/lib/chart.js
+++ b/app/assets/javascripts/lib/chart.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren */
/*= require Chart */
diff --git a/app/assets/javascripts/lib/cropper.js b/app/assets/javascripts/lib/cropper.js
index a88e640f298..5221f85ba7a 100644
--- a/app/assets/javascripts/lib/cropper.js
+++ b/app/assets/javascripts/lib/cropper.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren */
/*= require cropper */
diff --git a/app/assets/javascripts/lib/d3.js b/app/assets/javascripts/lib/d3.js
index ee1baf54803..57e7986576c 100644
--- a/app/assets/javascripts/lib/d3.js
+++ b/app/assets/javascripts/lib/d3.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren */
/*= require d3 */
diff --git a/app/assets/javascripts/lib/raphael.js b/app/assets/javascripts/lib/raphael.js
index 6df427bc2b1..5a9a501efe3 100644
--- a/app/assets/javascripts/lib/raphael.js
+++ b/app/assets/javascripts/lib/raphael.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren */
/*= require raphael */
/*= require g.raphael */
diff --git a/app/assets/javascripts/lib/utils/animate.js b/app/assets/javascripts/lib/utils/animate.js
index d36efdabc93..83957af94f3 100644
--- a/app/assets/javascripts/lib/utils/animate.js
+++ b/app/assets/javascripts/lib/utils/animate.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, no-void, prefer-template, no-var, new-cap, prefer-arrow-callback, consistent-return, padded-blocks, max-len */
(function() {
(function(w) {
if (w.gl == null) {
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 9299d0eabd2..d83c41fae9d 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-param-reassign, no-else-return, quotes, object-shorthand, comma-dangle, camelcase, one-var, vars-on-top, one-var-declaration-per-line, no-return-assign, consistent-return, padded-blocks, max-len */
(function() {
(function(w) {
var base;
@@ -23,6 +24,81 @@
return null;
}
};
+
+ w.gl.utils.ajaxGet = function(url) {
+ return $.ajax({
+ type: "GET",
+ url: url,
+ dataType: "script"
+ });
+ };
+
+ w.gl.utils.split = function(val) {
+ return val.split(/,\s*/);
+ };
+
+ w.gl.utils.extractLast = function(term) {
+ return this.split(term).pop();
+ };
+
+ w.gl.utils.rstrip = function rstrip(val) {
+ if (val) {
+ return val.replace(/\s+$/, '');
+ } else {
+ return val;
+ }
+ };
+
+ w.gl.utils.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) {
+ event_name = event_name || 'input';
+ var closest_submit, field, that;
+ that = this;
+ field = $(field_selector);
+ closest_submit = field.closest('form').find(button_selector);
+ if (this.rstrip(field.val()) === "") {
+ closest_submit.disable();
+ }
+ return field.on(event_name, function() {
+ if (that.rstrip($(this).val()) === "") {
+ return closest_submit.disable();
+ } else {
+ return closest_submit.enable();
+ }
+ });
+ };
+
+ w.gl.utils.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
+ var closest_submit, updateButtons;
+ closest_submit = form.find(button_selector);
+ updateButtons = function() {
+ var filled;
+ filled = true;
+ form.find('input').filter(form_selector).each(function() {
+ return filled = this.rstrip($(this).val()) !== "" || !$(this).attr('required');
+ });
+ if (filled) {
+ return closest_submit.enable();
+ } else {
+ return closest_submit.disable();
+ }
+ };
+ updateButtons();
+ return form.keyup(updateButtons);
+ };
+
+ w.gl.utils.sanitize = function(str) {
+ return str.replace(/<(?:.|\n)*?>/gm, '');
+ };
+
+ w.gl.utils.unbindEvents = function() {
+ return $(document).off('scroll');
+ };
+
+ w.gl.utils.shiftWindow = function() {
+ return w.scrollBy(0, -100);
+ };
+
+
gl.utils.updateTooltipTitle = function($tooltipEl, newTitle) {
return $tooltipEl.tooltip('destroy').attr('title', newTitle).tooltip('fixTitle');
};
@@ -38,23 +114,22 @@
gl.utils.getPagePath = function() {
return $('body').data('page').split(':')[0];
};
- return jQuery.timefor = function(time, suffix, expiredLabel) {
- var suffixFromNow, timefor;
- if (!time) {
- return '';
- }
- suffix || (suffix = 'remaining');
- expiredLabel || (expiredLabel = 'Past due');
- jQuery.timeago.settings.allowFuture = true;
- suffixFromNow = jQuery.timeago.settings.strings.suffixFromNow;
- jQuery.timeago.settings.strings.suffixFromNow = suffix;
- timefor = $.timeago(time);
- if (timefor.indexOf('ago') > -1) {
- timefor = expiredLabel;
- }
- jQuery.timeago.settings.strings.suffixFromNow = suffixFromNow;
- return timefor;
+ gl.utils.parseUrl = function (url) {
+ var parser = document.createElement('a');
+ parser.href = url;
+ return parser;
};
+ gl.utils.cleanupBeforeFetch = function() {
+ // Unbind scroll events
+ $(document).off('scroll');
+ // Close any open tooltips
+ $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy');
+ };
+
+ gl.utils.isMetaKey = function(e) {
+ return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
+ };
+
})(window);
}).call(this);
diff --git a/app/assets/javascripts/lib/utils/custom_event_polyfill.js.es6 b/app/assets/javascripts/lib/utils/custom_event_polyfill.js.es6
new file mode 100644
index 00000000000..5ae978010c9
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/custom_event_polyfill.js.es6
@@ -0,0 +1,12 @@
+/**
+ * CustomEvent support for IE
+ */
+if (typeof window.CustomEvent !== 'function') {
+ window.CustomEvent = function CustomEvent(e, params) {
+ const options = params || { bubbles: false, cancelable: false, detail: undefined };
+ const evt = document.createEvent('CustomEvent');
+ evt.initCustomEvent(e, options.bubbles, options.cancelable, options.detail);
+ return evt;
+ };
+ window.CustomEvent.prototype = window.Event.prototype;
+}
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 8fdf4646cd8..963d2851e5f 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -1,3 +1,10 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, padded-blocks, max-len */
+/* global timeago */
+/* global dateFormat */
+
+/*= require timeago */
+/*= require date.format */
+
(function() {
(function(w) {
var base;
@@ -21,51 +28,64 @@
if (setTimeago == null) {
setTimeago = true;
}
+
$timeagoEls.each(function() {
- var $el;
- $el = $(this);
- return $el.attr('title', gl.utils.formatDate($el.attr('datetime')));
+ var $el = $(this);
+ $el.attr('title', gl.utils.formatDate($el.attr('datetime')));
+
+ if (setTimeago) {
+ // Recreate with custom template
+ $el.tooltip({
+ template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
+ });
+ }
+ gl.utils.renderTimeago($el);
});
- if (setTimeago) {
- $timeagoEls.timeago();
- $timeagoEls.tooltip('destroy');
- // Recreate with custom template
- return $timeagoEls.tooltip({
- template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
- });
- }
};
- w.gl.utils.shortTimeAgo = function($el) {
- var shortLocale, tmpLocale;
- shortLocale = {
- prefixAgo: null,
- prefixFromNow: null,
- suffixAgo: 'ago',
- suffixFromNow: 'from now',
- seconds: '1 min',
- minute: '1 min',
- minutes: '%d mins',
- hour: '1 hr',
- hours: '%d hrs',
- day: '1 day',
- days: '%d days',
- month: '1 month',
- months: '%d months',
- year: '1 year',
- years: '%d years',
- wordSeparator: ' ',
- numbers: []
+ w.gl.utils.getTimeago = function() {
+ var locale = function(number, index) {
+ return [
+ ['less than a minute ago', 'a while'],
+ ['less than a minute ago', 'in %s seconds'],
+ ['about a minute ago', 'in 1 minute'],
+ ['%s minutes ago', 'in %s minutes'],
+ ['about an hour ago', 'in 1 hour'],
+ ['about %s hours ago', 'in %s hours'],
+ ['a day ago', 'in 1 day'],
+ ['%s days ago', 'in %s days'],
+ ['a week ago', 'in 1 week'],
+ ['%s weeks ago', 'in %s weeks'],
+ ['a month ago', 'in 1 month'],
+ ['%s months ago', 'in %s months'],
+ ['a year ago', 'in 1 year'],
+ ['%s years ago', 'in %s years']
+ ][index];
};
- tmpLocale = $.timeago.settings.strings;
- $el.each(function(el) {
- var $el1;
- $el1 = $(this);
- return $el1.attr('title', gl.utils.formatDate($el.attr('datetime')));
- });
- $.timeago.settings.strings = shortLocale;
- $el.timeago();
- $.timeago.settings.strings = tmpLocale;
+
+ timeago.register('gl_en', locale);
+ return timeago();
+ };
+
+ w.gl.utils.timeFor = function(time, suffix, expiredLabel) {
+ var timefor;
+ if (!time) {
+ return '';
+ }
+ suffix || (suffix = 'remaining');
+ expiredLabel || (expiredLabel = 'Past due');
+ timefor = gl.utils.getTimeago().format(time).replace('in', '');
+ if (timefor.indexOf('ago') > -1) {
+ timefor = expiredLabel;
+ } else {
+ timefor = timefor.trim() + ' ' + suffix;
+ }
+ return timefor;
+ };
+
+ w.gl.utils.renderTimeago = function($element) {
+ var timeagoInstance = gl.utils.getTimeago();
+ timeagoInstance.render($element, 'gl_en');
};
w.gl.utils.getDayDifference = function(a, b) {
@@ -74,7 +94,7 @@
var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
return Math.floor((date2 - date1) / millisecondsPerDay);
- }
+ };
})(window);
diff --git a/app/assets/javascripts/lib/utils/jquery.timeago.js b/app/assets/javascripts/lib/utils/jquery.timeago.js
deleted file mode 100644
index cc17aa7d3d1..00000000000
--- a/app/assets/javascripts/lib/utils/jquery.timeago.js
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * Timeago is a jQuery plugin that makes it easy to support automatically
- * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
- *
- * @name timeago
- * @version 1.1.0
- * @requires jQuery v1.2.3+
- * @author Ryan McGeary
- * @license MIT License - http://www.opensource.org/licenses/mit-license.php
- *
- * For usage and examples, visit:
- * http://timeago.yarp.com/
- *
- * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
- */
-
-(function (factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['jquery'], factory);
- } else {
- // Browser globals
- factory(jQuery);
- }
-}(function ($) {
- $.timeago = function(timestamp) {
- if (timestamp instanceof Date) {
- return inWords(timestamp);
- } else if (typeof timestamp === "string") {
- return inWords($.timeago.parse(timestamp));
- } else if (typeof timestamp === "number") {
- return inWords(new Date(timestamp));
- } else {
- return inWords($.timeago.datetime(timestamp));
- }
- };
- var $t = $.timeago;
-
- $.extend($.timeago, {
- settings: {
- refreshMillis: 60000,
- allowFuture: false,
- strings: {
- prefixAgo: null,
- prefixFromNow: null,
- suffixAgo: "ago",
- suffixFromNow: "from now",
- seconds: "less than a minute",
- minute: "about a minute",
- minutes: "%d minutes",
- hour: "about an hour",
- hours: "about %d hours",
- day: "a day",
- days: "%d days",
- month: "about a month",
- months: "%d months",
- year: "about a year",
- years: "%d years",
- wordSeparator: " ",
- numbers: []
- }
- },
- inWords: function(distanceMillis) {
- var $l = this.settings.strings;
- var prefix = $l.prefixAgo;
- var suffix = $l.suffixAgo;
- if (this.settings.allowFuture) {
- if (distanceMillis < 0) {
- prefix = $l.prefixFromNow;
- suffix = $l.suffixFromNow;
- }
- }
-
- var seconds = Math.abs(distanceMillis) / 1000;
- var minutes = seconds / 60;
- var hours = minutes / 60;
- var days = hours / 24;
- var years = days / 365;
-
- function substitute(stringOrFunction, number) {
- var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
- var value = ($l.numbers && $l.numbers[number]) || number;
- return string.replace(/%d/i, value);
- }
-
- var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
- seconds < 90 && substitute($l.minute, 1) ||
- minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
- minutes < 90 && substitute($l.hour, 1) ||
- hours < 24 && substitute($l.hours, Math.round(hours)) ||
- hours < 42 && substitute($l.day, 1) ||
- days < 30 && substitute($l.days, Math.round(days)) ||
- days < 45 && substitute($l.month, 1) ||
- days < 365 && substitute($l.months, Math.round(days / 30)) ||
- years < 1.5 && substitute($l.year, 1) ||
- substitute($l.years, Math.round(years));
-
- var separator = $l.wordSeparator || "";
- if ($l.wordSeparator === undefined) { separator = " "; }
- return $.trim([prefix, words, suffix].join(separator));
- },
- parse: function(iso8601) {
- var s = $.trim(iso8601);
- s = s.replace(/\.\d+/,""); // remove milliseconds
- s = s.replace(/-/,"/").replace(/-/,"/");
- s = s.replace(/T/," ").replace(/Z/," UTC");
- s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
- return new Date(s);
- },
- datetime: function(elem) {
- var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
- return $t.parse(iso8601);
- },
- isTime: function(elem) {
- // jQuery's `is()` doesn't play well with HTML5 in IE
- return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
- }
- });
-
- // functions that can be called via $(el).timeago('action')
- // init is default when no action is given
- // functions are called with context of a single element
- var functions = {
- init: function(){
- var refresh_el = $.proxy(refresh, this);
- refresh_el();
- var $s = $t.settings;
- if ($s.refreshMillis > 0) {
- setInterval(refresh_el, $s.refreshMillis);
- }
- },
- update: function(time){
- $(this).data('timeago', { datetime: $t.parse(time) });
- refresh.apply(this);
- }
- };
-
- $.fn.timeago = function(action, options) {
- var fn = action ? functions[action] : functions.init;
- if(!fn){
- throw new Error("Unknown function name '"+ action +"' for timeago");
- }
- // each over objects here and call the requested function
- this.each(function(){
- fn.call(this, options);
- });
- return this;
- };
-
- function refresh() {
- var data = prepareData(this);
- if (!isNaN(data.datetime)) {
- $(this).text(inWords(data.datetime));
- }
- return this;
- }
-
- function prepareData(element) {
- element = $(element);
- if (!element.data("timeago")) {
- element.data("timeago", { datetime: $t.datetime(element) });
- var text = $.trim(element.text());
- if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
- element.attr("title", text);
- }
- }
- return element.data("timeago");
- }
-
- function inWords(date) {
- return $t.inWords(distance(date));
- }
-
- function distance(date) {
- return (new Date().getTime() - date.getTime());
- }
-
- // fix for IE6 suckage
- document.createElement("abbr");
- document.createElement("time");
-}));
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index 5b338b00d76..d0fe69260a5 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, consistent-return, no-undef, prefer-arrow-callback, no-return-assign, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, max-len */
(function() {
(function(w) {
var notificationGranted, notifyMe, notifyPermissions;
diff --git a/app/assets/javascripts/lib/utils/pretty_time.js.es6 b/app/assets/javascripts/lib/utils/pretty_time.js.es6
new file mode 100644
index 00000000000..ccaf447eb0b
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/pretty_time.js.es6
@@ -0,0 +1,67 @@
+(() => {
+ /*
+ * TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints,
+ * stringifyTime condensed or non-condensed, abbreviateTimelengths)
+ * */
+
+ class PrettyTime {
+
+ /*
+ * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # }
+ * Seconds can be negative or positive, zero or non-zero.
+ */
+ static parseSeconds(seconds) {
+ const DAYS_PER_WEEK = 5;
+ const HOURS_PER_DAY = 8;
+ 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 = PrettyTime.secondsToMinutes(seconds);
+
+ return _.mapObject(timePeriodConstraints, (minutesPerPeriod) => {
+ const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod);
+
+ unorderedMinutes -= (periodCount * minutesPerPeriod);
+
+ return periodCount;
+ });
+ }
+
+ /*
+ * Accepts a timeObject and returns a condensed string representation of it
+ * (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included.
+ */
+
+ static 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.
+ */
+
+ static abbreviateTime(timeStr) {
+ return timeStr.split(' ')
+ .filter(unitStr => unitStr.charAt(0) !== '0')[0];
+ }
+
+ static secondsToMinutes(seconds) {
+ return Math.abs(seconds / 60);
+ }
+ }
+
+ gl.PrettyTime = PrettyTime;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index d761a844be9..ac44b81ee22 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, semi, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, padded-blocks, max-len */
(function() {
(function(w) {
var base;
@@ -7,6 +8,9 @@
if ((base = w.gl).text == null) {
base.text = {};
}
+ gl.text.addDelimiter = function(text) {
+ return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
+ }
gl.text.randomString = function() {
return Math.random().toString(36).substring(7);
};
@@ -108,6 +112,9 @@
gl.text.removeListeners = function(form) {
return $('.js-md', form).off();
};
+ gl.text.humanize = function(string) {
+ return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
+ }
return gl.text.truncate = function(string, maxLength) {
return string.substr(0, (maxLength - 3)) + '...';
};
diff --git a/app/assets/javascripts/lib/utils/type_utility.js b/app/assets/javascripts/lib/utils/type_utility.js
index dc30babd645..961859dfb5b 100644
--- a/app/assets/javascripts/lib/utils/type_utility.js
+++ b/app/assets/javascripts/lib/utils/type_utility.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-return-assign, padded-blocks, max-len */
(function() {
(function(w) {
var base;
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index f84a20cf0fe..6872186cd7f 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, no-plusplus, guard-for-in, no-restricted-syntax, prefer-template, quotes, padded-blocks, max-len */
(function() {
(function(w) {
var base;
@@ -19,7 +20,7 @@
while (i < sURLVariables.length) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
- values.push(sParameterName[1]);
+ values.push(sParameterName[1].replace(/\+/g, ' '));
}
i++;
}
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index 93daea1dce7..b0f834705c3 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-underscore-dangle, no-param-reassign, no-undef, prefer-template, quotes, comma-dangle, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, spaced-comment, radix, no-else-return, max-len, no-plusplus, padded-blocks, max-len */
// LineHighlighter
//
// Handles single- and multi-line selection and highlight for blob views.
diff --git a/app/assets/javascripts/logo.js b/app/assets/javascripts/logo.js
index 7d8eef1b495..0ae6df311bb 100644
--- a/app/assets/javascripts/logo.js
+++ b/app/assets/javascripts/logo.js
@@ -1,3 +1,6 @@
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, padded-blocks */
+/* global Turbolinks */
+
(function() {
Turbolinks.enableProgressBar();
diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js
index 1935af491f7..7741cd29793 100644
--- a/app/assets/javascripts/member_expiration_date.js
+++ b/app/assets/javascripts/member_expiration_date.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, vars-on-top, no-var, object-shorthand, comma-dangle, max-len */
(function() {
// 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
@@ -14,14 +15,18 @@
inputs.datepicker({
dateFormat: 'yy-mm-dd',
minDate: 1,
- onSelect: toggleClearInput
+ onSelect: function () {
+ $(this).trigger('change');
+ toggleClearInput.call(this);
+ }
});
inputs.next('.js-clear-input').on('click', function(event) {
event.preventDefault();
var input = $(this).closest('.clearable-input').find('.js-access-expiration-date');
- input.datepicker('setDate', null);
+ input.datepicker('setDate', null)
+ .trigger('change');
toggleClearInput.call(input);
});
diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6
new file mode 100644
index 00000000000..895bc10784f
--- /dev/null
+++ b/app/assets/javascripts/members.js.es6
@@ -0,0 +1,38 @@
+/* eslint-disable */
+((w) => {
+ w.gl = w.gl || {};
+
+ class Members {
+ constructor() {
+ this.addListeners();
+ }
+
+ addListeners() {
+ $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow);
+ $('.js-member-update-control').off('change').on('change', this.formSubmit);
+ $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess);
+ gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
+ }
+
+ removeRow(e) {
+ const $target = $(e.target);
+
+ if ($target.hasClass('btn-remove')) {
+ $target.closest('.member')
+ .fadeOut(function () {
+ $(this).remove();
+ });
+ }
+ }
+
+ formSubmit() {
+ $(this).closest('form').trigger("submit.rails").end().disable();
+ }
+
+ formSuccess() {
+ $(this).find('.js-member-update-control').enable();
+ }
+ }
+
+ gl.Members = Members;
+})(window);
diff --git a/app/assets/javascripts/merge_conflict_data_provider.js.es6 b/app/assets/javascripts/merge_conflict_data_provider.js.es6
deleted file mode 100644
index cd92df8ddc5..00000000000
--- a/app/assets/javascripts/merge_conflict_data_provider.js.es6
+++ /dev/null
@@ -1,341 +0,0 @@
-const HEAD_HEADER_TEXT = 'HEAD//our changes';
-const ORIGIN_HEADER_TEXT = 'origin//their changes';
-const HEAD_BUTTON_TITLE = 'Use ours';
-const ORIGIN_BUTTON_TITLE = 'Use theirs';
-
-
-class MergeConflictDataProvider {
-
- getInitialData() {
- const diffViewType = $.cookie('diff_view');
-
- return {
- isLoading : true,
- hasError : false,
- isParallel : diffViewType === 'parallel',
- diffViewType : diffViewType,
- isSubmitting : false,
- conflictsData : {},
- resolutionData : {}
- }
- }
-
-
- decorateData(vueInstance, data) {
- this.vueInstance = vueInstance;
-
- if (data.type === 'error') {
- vueInstance.hasError = true;
- data.errorMessage = data.message;
- }
- else {
- data.shortCommitSha = data.commit_sha.slice(0, 7);
- data.commitMessage = data.commit_message;
-
- this.setParallelLines(data);
- this.setInlineLines(data);
- this.updateResolutionsData(data);
- }
-
- vueInstance.conflictsData = data;
- vueInstance.isSubmitting = false;
-
- const conflictsText = this.getConflictsCount() > 1 ? 'conflicts' : 'conflict';
- vueInstance.conflictsData.conflictsText = conflictsText;
- }
-
-
- updateResolutionsData(data) {
- const vi = this.vueInstance;
-
- data.files.forEach( (file) => {
- file.sections.forEach( (section) => {
- if (section.conflict) {
- vi.$set(`resolutionData['${section.id}']`, false);
- }
- });
- });
- }
-
-
- setParallelLines(data) {
- data.files.forEach( (file) => {
- file.filePath = this.getFilePath(file);
- file.iconClass = `fa-${file.blob_icon}`;
- file.blobPath = file.blob_path;
- file.parallelLines = [];
- const linesObj = { left: [], right: [] };
-
- file.sections.forEach( (section) => {
- const { conflict, lines, id } = section;
-
- if (conflict) {
- linesObj.left.push(this.getOriginHeaderLine(id));
- linesObj.right.push(this.getHeadHeaderLine(id));
- }
-
- lines.forEach( (line) => {
- const { type } = line;
-
- if (conflict) {
- if (type === 'old') {
- linesObj.left.push(this.getLineForParallelView(line, id, 'conflict'));
- }
- else if (type === 'new') {
- linesObj.right.push(this.getLineForParallelView(line, id, 'conflict', true));
- }
- }
- else {
- const lineType = type || 'context';
-
- linesObj.left.push (this.getLineForParallelView(line, id, lineType));
- linesObj.right.push(this.getLineForParallelView(line, id, lineType, true));
- }
- });
-
- this.checkLineLengths(linesObj);
- });
-
- for (let i = 0, len = linesObj.left.length; i < len; i++) {
- file.parallelLines.push([
- linesObj.right[i],
- linesObj.left[i]
- ]);
- }
-
- });
- }
-
-
- checkLineLengths(linesObj) {
- let { left, right } = linesObj;
-
- if (left.length !== right.length) {
- if (left.length > right.length) {
- const diff = left.length - right.length;
- for (let i = 0; i < diff; i++) {
- right.push({ lineType: 'emptyLine', richText: '' });
- }
- }
- else {
- const diff = right.length - left.length;
- for (let i = 0; i < diff; i++) {
- left.push({ lineType: 'emptyLine', richText: '' });
- }
- }
- }
- }
-
-
- setInlineLines(data) {
- data.files.forEach( (file) => {
- file.iconClass = `fa-${file.blob_icon}`;
- file.blobPath = file.blob_path;
- file.filePath = this.getFilePath(file);
- file.inlineLines = []
-
- file.sections.forEach( (section) => {
- let currentLineType = 'new';
- const { conflict, lines, id } = section;
-
- if (conflict) {
- file.inlineLines.push(this.getHeadHeaderLine(id));
- }
-
- lines.forEach( (line) => {
- const { type } = line;
-
- if ((type === 'new' || type === 'old') && currentLineType !== type) {
- currentLineType = type;
- file.inlineLines.push({ lineType: 'emptyLine', richText: '' });
- }
-
- this.decorateLineForInlineView(line, id, conflict);
- file.inlineLines.push(line);
- })
-
- if (conflict) {
- file.inlineLines.push(this.getOriginHeaderLine(id));
- }
- });
- });
- }
-
-
- handleSelected(sectionId, selection) {
- const vi = this.vueInstance;
-
- vi.resolutionData[sectionId] = selection;
- vi.conflictsData.files.forEach( (file) => {
- file.inlineLines.forEach( (line) => {
- if (line.id === sectionId && (line.hasConflict || line.isHeader)) {
- this.markLine(line, selection);
- }
- });
-
- file.parallelLines.forEach( (lines) => {
- const left = lines[0];
- const right = lines[1];
- const hasSameId = right.id === sectionId || left.id === sectionId;
- const isLeftMatch = left.hasConflict || left.isHeader;
- const isRightMatch = right.hasConflict || right.isHeader;
-
- if (hasSameId && (isLeftMatch || isRightMatch)) {
- this.markLine(left, selection);
- this.markLine(right, selection);
- }
- })
- });
- }
-
-
- updateViewType(newType) {
- const vi = this.vueInstance;
-
- if (newType === vi.diffView || !(newType === 'parallel' || newType === 'inline')) {
- return;
- }
-
- vi.diffView = newType;
- vi.isParallel = newType === 'parallel';
- $.cookie('diff_view', newType); // TODO: Make sure that cookie path added.
- $('.content-wrapper .container-fluid').toggleClass('container-limited');
- }
-
-
- markLine(line, selection) {
- if (selection === 'head' && line.isHead) {
- line.isSelected = true;
- line.isUnselected = false;
- }
- else if (selection === 'origin' && line.isOrigin) {
- line.isSelected = true;
- line.isUnselected = false;
- }
- else {
- line.isSelected = false;
- line.isUnselected = true;
- }
- }
-
-
- getConflictsCount() {
- return Object.keys(this.vueInstance.resolutionData).length;
- }
-
-
- getResolvedCount() {
- let count = 0;
- const data = this.vueInstance.resolutionData;
-
- for (const id in data) {
- const resolution = data[id];
- if (resolution) {
- count++;
- }
- }
-
- return count;
- }
-
-
- isReadyToCommit() {
- const { conflictsData, isSubmitting } = this.vueInstance
- const allResolved = this.getConflictsCount() === this.getResolvedCount();
- const hasCommitMessage = $.trim(conflictsData.commitMessage).length;
-
- return !isSubmitting && hasCommitMessage && allResolved;
- }
-
-
- getCommitButtonText() {
- const initial = 'Commit conflict resolution';
- const inProgress = 'Committing...';
- const vue = this.vueInstance;
-
- return vue ? vue.isSubmitting ? inProgress : initial : initial;
- }
-
-
- decorateLineForInlineView(line, id, conflict) {
- const { type } = line;
- line.id = id;
- line.hasConflict = conflict;
- line.isHead = type === 'new';
- line.isOrigin = type === 'old';
- line.hasMatch = type === 'match';
- line.richText = line.rich_text;
- line.isSelected = false;
- line.isUnselected = false;
- }
-
- getLineForParallelView(line, id, lineType, isHead) {
- const { old_line, new_line, rich_text } = line;
- const hasConflict = lineType === 'conflict';
-
- return {
- id,
- lineType,
- hasConflict,
- isHead : hasConflict && isHead,
- isOrigin : hasConflict && !isHead,
- hasMatch : lineType === 'match',
- lineNumber : isHead ? new_line : old_line,
- section : isHead ? 'head' : 'origin',
- richText : rich_text,
- isSelected : false,
- isUnselected : false
- }
- }
-
-
- getHeadHeaderLine(id) {
- return {
- id : id,
- richText : HEAD_HEADER_TEXT,
- buttonTitle : HEAD_BUTTON_TITLE,
- type : 'new',
- section : 'head',
- isHeader : true,
- isHead : true,
- isSelected : false,
- isUnselected: false
- }
- }
-
-
- getOriginHeaderLine(id) {
- return {
- id : id,
- richText : ORIGIN_HEADER_TEXT,
- buttonTitle : ORIGIN_BUTTON_TITLE,
- type : 'old',
- section : 'origin',
- isHeader : true,
- isOrigin : true,
- isSelected : false,
- isUnselected: false
- }
- }
-
-
- handleFailedRequest(vueInstance, data) {
- vueInstance.hasError = true;
- vueInstance.conflictsData.errorMessage = 'Something went wrong!';
- }
-
-
- getCommitData() {
- return {
- commit_message: this.vueInstance.conflictsData.commitMessage,
- sections: this.vueInstance.resolutionData
- }
- }
-
-
- getFilePath(file) {
- const { old_path, new_path } = file;
- return old_path === new_path ? new_path : `${old_path} → ${new_path}`;
- }
-
-}
diff --git a/app/assets/javascripts/merge_conflict_resolver.js.es6 b/app/assets/javascripts/merge_conflict_resolver.js.es6
deleted file mode 100644
index b56fd5aa658..00000000000
--- a/app/assets/javascripts/merge_conflict_resolver.js.es6
+++ /dev/null
@@ -1,83 +0,0 @@
-//= require vue
-
-class MergeConflictResolver {
-
- constructor() {
- this.dataProvider = new MergeConflictDataProvider()
- this.initVue()
- }
-
-
- initVue() {
- const that = this;
- this.vue = new Vue({
- el : '#conflicts',
- name : 'MergeConflictResolver',
- data : this.dataProvider.getInitialData(),
- created : this.fetchData(),
- computed : this.setComputedProperties(),
- methods : {
- handleSelected(sectionId, selection) {
- that.dataProvider.handleSelected(sectionId, selection);
- },
- handleViewTypeChange(newType) {
- that.dataProvider.updateViewType(newType);
- },
- commit() {
- that.commit();
- }
- }
- })
- }
-
-
- setComputedProperties() {
- const dp = this.dataProvider;
-
- return {
- conflictsCount() { return dp.getConflictsCount() },
- resolvedCount() { return dp.getResolvedCount() },
- readyToCommit() { return dp.isReadyToCommit() },
- commitButtonText() { return dp.getCommitButtonText() }
- }
- }
-
-
- fetchData() {
- const dp = this.dataProvider;
-
- $.get($('#conflicts').data('conflictsPath'))
- .done((data) => {
- dp.decorateData(this.vue, data);
- })
- .error((data) => {
- dp.handleFailedRequest(this.vue, data);
- })
- .always(() => {
- this.vue.isLoading = false;
-
- this.vue.$nextTick(() => {
- $('#conflicts .js-syntax-highlight').syntaxHighlight();
- });
-
- if (this.vue.diffViewType === 'parallel') {
- $('.content-wrapper .container-fluid').removeClass('container-limited');
- }
- })
- }
-
-
- commit() {
- this.vue.isSubmitting = true;
-
- $.post($('#conflicts').data('resolveConflictsPath'), this.dataProvider.getCommitData())
- .done((data) => {
- window.location.href = data.redirect_to;
- })
- .error(() => {
- this.vue.isSubmitting = false;
- new Flash('Something went wrong!');
- });
- }
-
-}
diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
new file mode 100644
index 00000000000..9e4ffd07dbd
--- /dev/null
+++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
@@ -0,0 +1,94 @@
+/* eslint-disable */
+((global) => {
+
+ global.mergeConflicts = global.mergeConflicts || {};
+
+ global.mergeConflicts.diffFileEditor = Vue.extend({
+ props: {
+ file: Object,
+ onCancelDiscardConfirmation: Function,
+ onAcceptDiscardConfirmation: Function
+ },
+ data() {
+ return {
+ saved: false,
+ loading: false,
+ fileLoaded: false,
+ originalContent: '',
+ }
+ },
+ computed: {
+ classObject() {
+ return {
+ 'saved': this.saved,
+ 'is-loading': this.loading
+ };
+ }
+ },
+ watch: {
+ ['file.showEditor'](val) {
+ this.resetEditorContent();
+
+ if (!val || this.fileLoaded || this.loading) {
+ return;
+ }
+
+ this.loadEditor();
+ }
+ },
+ mounted() {
+ if (this.file.loadEditor) {
+ this.loadEditor();
+ }
+ },
+ methods: {
+ loadEditor() {
+ this.loading = true;
+
+ $.get(this.file.content_path)
+ .done((file) => {
+ let content = this.$el.querySelector('pre');
+ let fileContent = document.createTextNode(file.content);
+
+ content.textContent = fileContent.textContent;
+
+ this.originalContent = file.content;
+ this.fileLoaded = true;
+ this.editor = ace.edit(content);
+ this.editor.$blockScrolling = Infinity; // Turn off annoying warning
+ this.editor.getSession().setMode(`ace/mode/${file.blob_ace_mode}`);
+ this.editor.on('change', () => {
+ this.saveDiffResolution();
+ });
+ this.saveDiffResolution();
+ })
+ .fail(() => {
+ new Flash('Failed to load the file, please try again.');
+ })
+ .always(() => {
+ this.loading = false;
+ });
+ },
+ saveDiffResolution() {
+ this.saved = true;
+
+ // This probably be better placed in the data provider
+ this.file.content = this.editor.getValue();
+ this.file.resolveEditChanged = this.file.content !== this.originalContent;
+ this.file.promptDiscardConfirmation = false;
+ },
+ resetEditorContent() {
+ if (this.fileLoaded) {
+ this.editor.setValue(this.originalContent, -1);
+ }
+ },
+ cancelDiscardConfirmation(file) {
+ this.onCancelDiscardConfirmation(file);
+ },
+ acceptDiscardConfirmation(file) {
+ this.onAcceptDiscardConfirmation(file);
+ }
+ }
+ });
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6 b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6
new file mode 100644
index 00000000000..23c4618af70
--- /dev/null
+++ b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6
@@ -0,0 +1,13 @@
+/* eslint-disable */
+((global) => {
+
+ global.mergeConflicts = global.mergeConflicts || {};
+
+ global.mergeConflicts.inlineConflictLines = Vue.extend({
+ props: {
+ file: Object
+ },
+ mixins: [global.mergeConflicts.utils, global.mergeConflicts.actions],
+ });
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6 b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6
new file mode 100644
index 00000000000..4ccbdcd6daa
--- /dev/null
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6
@@ -0,0 +1,28 @@
+/* eslint-disable */
+((global) => {
+
+ global.mergeConflicts = global.mergeConflicts || {};
+
+ global.mergeConflicts.parallelConflictLines = Vue.extend({
+ props: {
+ file: Object
+ },
+ mixins: [global.mergeConflicts.utils, global.mergeConflicts.actions],
+ template: `
+ <table>
+ <tr class="line_holder parallel" v-for="section in file.parallelLines">
+ <template v-for="line in section">
+ <td class="diff-line-num header" :class="lineCssClass(line)" v-if="line.isHeader"></td>
+ <td class="line_content header" :class="lineCssClass(line)" v-if="line.isHeader">
+ <strong>{{line.richText}}</strong>
+ <button class="btn" @click="handleSelected(file, line.id, line.section)">{{line.buttonTitle}}</button>
+ </td>
+ <td class="diff-line-num old_line" :class="lineCssClass(line)" v-if="!line.isHeader">{{line.lineNumber}}</td>
+ <td class="line_content parallel" :class="lineCssClass(line)" v-if="!line.isHeader" v-html="line.richText"></td>
+ </template>
+ </tr>
+ </table>
+ `,
+ });
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6
new file mode 100644
index 00000000000..8a7519b0786
--- /dev/null
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6
@@ -0,0 +1,31 @@
+/* eslint-disable */
+((global) => {
+ global.mergeConflicts = global.mergeConflicts || {};
+
+ class mergeConflictsService {
+ constructor(options) {
+ this.conflictsPath = options.conflictsPath;
+ this.resolveConflictsPath = options.resolveConflictsPath;
+ }
+
+ fetchConflictsData() {
+ return $.ajax({
+ dataType: 'json',
+ url: this.conflictsPath
+ });
+ }
+
+ submitResolveConflicts(data) {
+ return $.ajax({
+ url: this.resolveConflictsPath,
+ data: JSON.stringify(data),
+ contentType: 'application/json',
+ dataType: 'json',
+ method: 'POST'
+ });
+ }
+ };
+
+ global.mergeConflicts.mergeConflictsService = mergeConflictsService;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6
new file mode 100644
index 00000000000..f94e51e783c
--- /dev/null
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6
@@ -0,0 +1,436 @@
+/* eslint-disable */
+((global) => {
+ global.mergeConflicts = global.mergeConflicts || {};
+
+ const diffViewType = Cookies.get('diff_view');
+ const HEAD_HEADER_TEXT = 'HEAD//our changes';
+ const ORIGIN_HEADER_TEXT = 'origin//their changes';
+ const HEAD_BUTTON_TITLE = 'Use ours';
+ const ORIGIN_BUTTON_TITLE = 'Use theirs';
+ const INTERACTIVE_RESOLVE_MODE = 'interactive';
+ const EDIT_RESOLVE_MODE = 'edit';
+ const DEFAULT_RESOLVE_MODE = INTERACTIVE_RESOLVE_MODE;
+ const VIEW_TYPES = {
+ INLINE: 'inline',
+ PARALLEL: 'parallel'
+ };
+ const CONFLICT_TYPES = {
+ TEXT: 'text',
+ TEXT_EDITOR: 'text-editor'
+ };
+
+ global.mergeConflicts.mergeConflictsStore = {
+ state: {
+ isLoading: true,
+ hasError: false,
+ isSubmitting: false,
+ isParallel: diffViewType === VIEW_TYPES.PARALLEL,
+ diffViewType: diffViewType,
+ conflictsData: {}
+ },
+
+ setConflictsData(data) {
+ this.decorateFiles(data.files);
+
+ this.state.conflictsData = {
+ files: data.files,
+ commitMessage: data.commit_message,
+ sourceBranch: data.source_branch,
+ targetBranch: data.target_branch,
+ commitMessage: data.commit_message,
+ shortCommitSha: data.commit_sha.slice(0, 7),
+ };
+ },
+
+ decorateFiles(files) {
+ files.forEach((file) => {
+ file.content = '';
+ file.resolutionData = {};
+ file.promptDiscardConfirmation = false;
+ file.resolveMode = DEFAULT_RESOLVE_MODE;
+ file.filePath = this.getFilePath(file);
+ file.iconClass = `fa-${file.blob_icon}`;
+ file.blobPath = file.blob_path;
+
+ if (file.type === CONFLICT_TYPES.TEXT) {
+ file.showEditor = false;
+ file.loadEditor = false;
+
+ this.setInlineLine(file);
+ this.setParallelLine(file);
+ } else if (file.type === CONFLICT_TYPES.TEXT_EDITOR) {
+ file.showEditor = true;
+ file.loadEditor = true;
+ }
+ });
+ },
+
+ setInlineLine(file) {
+ file.inlineLines = [];
+
+ file.sections.forEach((section) => {
+ let currentLineType = 'new';
+ const { conflict, lines, id } = section;
+
+ if (conflict) {
+ file.inlineLines.push(this.getHeadHeaderLine(id));
+ }
+
+ lines.forEach((line) => {
+ const { type } = line;
+
+ if ((type === 'new' || type === 'old') && currentLineType !== type) {
+ currentLineType = type;
+ file.inlineLines.push({ lineType: 'emptyLine', richText: '' });
+ }
+
+ this.decorateLineForInlineView(line, id, conflict);
+ file.inlineLines.push(line);
+ })
+
+ if (conflict) {
+ file.inlineLines.push(this.getOriginHeaderLine(id));
+ }
+ });
+ },
+
+ setParallelLine(file) {
+ file.parallelLines = [];
+ const linesObj = { left: [], right: [] };
+
+ file.sections.forEach((section) => {
+ const { conflict, lines, id } = section;
+
+ if (conflict) {
+ linesObj.left.push(this.getOriginHeaderLine(id));
+ linesObj.right.push(this.getHeadHeaderLine(id));
+ }
+
+ lines.forEach((line) => {
+ const { type } = line;
+
+ if (conflict) {
+ if (type === 'old') {
+ linesObj.left.push(this.getLineForParallelView(line, id, 'conflict'));
+ } else if (type === 'new') {
+ linesObj.right.push(this.getLineForParallelView(line, id, 'conflict', true));
+ }
+ } else {
+ const lineType = type || 'context';
+
+ linesObj.left.push (this.getLineForParallelView(line, id, lineType));
+ linesObj.right.push(this.getLineForParallelView(line, id, lineType, true));
+ }
+ });
+
+ this.checkLineLengths(linesObj);
+ });
+
+ for (let i = 0, len = linesObj.left.length; i < len; i++) {
+ file.parallelLines.push([
+ linesObj.right[i],
+ linesObj.left[i]
+ ]);
+ }
+ },
+
+ setLoadingState(state) {
+ this.state.isLoading = state;
+ },
+
+ setErrorState(state) {
+ this.state.hasError = state;
+ },
+
+ setFailedRequest(message) {
+ this.state.hasError = true;
+ this.state.conflictsData.errorMessage = message;
+ },
+
+ getConflictsCount() {
+ if (!this.state.conflictsData.files.length) {
+ return 0;
+ }
+
+ const files = this.state.conflictsData.files;
+ let count = 0;
+
+ files.forEach((file) => {
+ if (file.type === CONFLICT_TYPES.TEXT) {
+ file.sections.forEach((section) => {
+ if (section.conflict) {
+ count++;
+ }
+ });
+ } else {
+ count++;
+ }
+ });
+
+ return count;
+ },
+
+ getConflictsCountText() {
+ const count = this.getConflictsCount();
+ const text = count ? 'conflicts' : 'conflict';
+
+ return `${count} ${text}`;
+ },
+
+ setViewType(viewType) {
+ this.state.diffView = viewType;
+ this.state.isParallel = viewType === VIEW_TYPES.PARALLEL;
+
+ Cookies.set('diff_view', viewType);
+ },
+
+ getHeadHeaderLine(id) {
+ return {
+ id: id,
+ richText: HEAD_HEADER_TEXT,
+ buttonTitle: HEAD_BUTTON_TITLE,
+ type: 'new',
+ section: 'head',
+ isHeader: true,
+ isHead: true,
+ isSelected: false,
+ isUnselected: false
+ };
+ },
+
+ decorateLineForInlineView(line, id, conflict) {
+ const { type } = line;
+ line.id = id;
+ line.hasConflict = conflict;
+ line.isHead = type === 'new';
+ line.isOrigin = type === 'old';
+ line.hasMatch = type === 'match';
+ line.richText = line.rich_text;
+ line.isSelected = false;
+ line.isUnselected = false;
+ },
+
+ getLineForParallelView(line, id, lineType, isHead) {
+ const { old_line, new_line, rich_text } = line;
+ const hasConflict = lineType === 'conflict';
+
+ return {
+ id,
+ lineType,
+ hasConflict,
+ isHead: hasConflict && isHead,
+ isOrigin: hasConflict && !isHead,
+ hasMatch: lineType === 'match',
+ lineNumber: isHead ? new_line : old_line,
+ section: isHead ? 'head' : 'origin',
+ richText: rich_text,
+ isSelected: false,
+ isUnselected: false
+ };
+ },
+
+ getOriginHeaderLine(id) {
+ return {
+ id: id,
+ richText: ORIGIN_HEADER_TEXT,
+ buttonTitle: ORIGIN_BUTTON_TITLE,
+ type: 'old',
+ section: 'origin',
+ isHeader: true,
+ isOrigin: true,
+ isSelected: false,
+ isUnselected: false
+ };
+ },
+
+ getFilePath(file) {
+ const { old_path, new_path } = file;
+ return old_path === new_path ? new_path : `${old_path} → ${new_path}`;
+ },
+
+ checkLineLengths(linesObj) {
+ let { left, right } = linesObj;
+
+ if (left.length !== right.length) {
+ if (left.length > right.length) {
+ const diff = left.length - right.length;
+ for (let i = 0; i < diff; i++) {
+ right.push({ lineType: 'emptyLine', richText: '' });
+ }
+ } else {
+ const diff = right.length - left.length;
+ for (let i = 0; i < diff; i++) {
+ left.push({ lineType: 'emptyLine', richText: '' });
+ }
+ }
+ }
+ },
+
+ setPromptConfirmationState(file, state) {
+ file.promptDiscardConfirmation = state;
+ },
+
+ setFileResolveMode(file, mode) {
+ if (mode === INTERACTIVE_RESOLVE_MODE) {
+ file.showEditor = false;
+ } else if (mode === EDIT_RESOLVE_MODE) {
+ // Restore Interactive mode when switching to Edit mode
+ file.showEditor = true;
+ file.loadEditor = true;
+ file.resolutionData = {};
+
+ this.restoreFileLinesState(file);
+ }
+
+ file.resolveMode = mode;
+ },
+
+ restoreFileLinesState(file) {
+ file.inlineLines.forEach((line) => {
+ if (line.hasConflict || line.isHeader) {
+ line.isSelected = false;
+ line.isUnselected = false;
+ }
+ });
+
+ file.parallelLines.forEach((lines) => {
+ const left = lines[0];
+ const right = lines[1];
+ const isLeftMatch = left.hasConflict || left.isHeader;
+ const isRightMatch = right.hasConflict || right.isHeader;
+
+ if (isLeftMatch || isRightMatch) {
+ left.isSelected = false;
+ left.isUnselected = false;
+ right.isSelected = false;
+ right.isUnselected = false;
+ }
+ });
+ },
+
+ isReadyToCommit() {
+ const files = this.state.conflictsData.files;
+ const hasCommitMessage = $.trim(this.state.conflictsData.commitMessage).length;
+ let unresolved = 0;
+
+ for (let i = 0, l = files.length; i < l; i++) {
+ let file = files[i];
+
+ if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) {
+ let numberConflicts = 0;
+ let resolvedConflicts = Object.keys(file.resolutionData).length
+
+ // We only check for conflicts type 'text'
+ // since conflicts `text_editor` can´t be resolved in interactive mode
+ if (file.type === CONFLICT_TYPES.TEXT) {
+ for (let j = 0, k = file.sections.length; j < k; j++) {
+ if (file.sections[j].conflict) {
+ numberConflicts++;
+ }
+ }
+
+ if (resolvedConflicts !== numberConflicts) {
+ unresolved++;
+ }
+ }
+ } else if (file.resolveMode === EDIT_RESOLVE_MODE) {
+
+ // Unlikely to happen since switching to Edit mode saves content automatically.
+ // Checking anyway in case the save strategy changes in the future
+ if (!file.content) {
+ unresolved++;
+ continue;
+ }
+ }
+ }
+
+ return !this.state.isSubmitting && hasCommitMessage && !unresolved;
+ },
+
+ getCommitButtonText() {
+ const initial = 'Commit conflict resolution';
+ const inProgress = 'Committing...';
+
+ return this.state ? this.state.isSubmitting ? inProgress : initial : initial;
+ },
+
+ getCommitData() {
+ let commitData = {};
+
+ commitData = {
+ commit_message: this.state.conflictsData.commitMessage,
+ files: []
+ };
+
+ this.state.conflictsData.files.forEach((file) => {
+ let addFile;
+
+ addFile = {
+ old_path: file.old_path,
+ new_path: file.new_path
+ };
+
+ if (file.type === CONFLICT_TYPES.TEXT) {
+
+ // Submit only one data for type of editing
+ if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) {
+ addFile.sections = file.resolutionData;
+ } else if (file.resolveMode === EDIT_RESOLVE_MODE) {
+ addFile.content = file.content;
+ }
+ } else if (file.type === CONFLICT_TYPES.TEXT_EDITOR) {
+ addFile.content = file.content;
+ }
+
+ commitData.files.push(addFile);
+ });
+
+ return commitData;
+ },
+
+ handleSelected(file, sectionId, selection) {
+ Vue.set(file.resolutionData, sectionId, selection);
+
+ file.inlineLines.forEach((line) => {
+ if (line.id === sectionId && (line.hasConflict || line.isHeader)) {
+ this.markLine(line, selection);
+ }
+ });
+
+ file.parallelLines.forEach((lines) => {
+ const left = lines[0];
+ const right = lines[1];
+ const hasSameId = right.id === sectionId || left.id === sectionId;
+ const isLeftMatch = left.hasConflict || left.isHeader;
+ const isRightMatch = right.hasConflict || right.isHeader;
+
+ if (hasSameId && (isLeftMatch || isRightMatch)) {
+ this.markLine(left, selection);
+ this.markLine(right, selection);
+ }
+ });
+ },
+
+ markLine(line, selection) {
+ if (selection === 'head' && line.isHead) {
+ line.isSelected = true;
+ line.isUnselected = false;
+ } else if (selection === 'origin' && line.isOrigin) {
+ line.isSelected = true;
+ line.isUnselected = false;
+ } else {
+ line.isSelected = false;
+ line.isUnselected = true;
+ }
+ },
+
+ setSubmitState(state) {
+ this.state.isSubmitting = state;
+ },
+
+ fileTextTypePresent() {
+ return this.state.conflictsData.files.some(f => f.type === CONFLICT_TYPES.TEXT);
+ }
+ };
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6
new file mode 100644
index 00000000000..815443fb54e
--- /dev/null
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6
@@ -0,0 +1,89 @@
+/* eslint-disable */
+//= require vue
+//= require ./merge_conflict_store
+//= require ./merge_conflict_service
+//= require ./mixins/line_conflict_utils
+//= require ./mixins/line_conflict_actions
+//= require ./components/diff_file_editor
+//= require ./components/inline_conflict_lines
+//= require ./components/parallel_conflict_lines
+
+$(() => {
+ const INTERACTIVE_RESOLVE_MODE = 'interactive';
+ const conflictsEl = document.querySelector('#conflicts');
+ const mergeConflictsStore = gl.mergeConflicts.mergeConflictsStore;
+ const mergeConflictsService = new gl.mergeConflicts.mergeConflictsService({
+ conflictsPath: conflictsEl.dataset.conflictsPath,
+ resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath
+ });
+
+ gl.MergeConflictsResolverApp = new Vue({
+ el: '#conflicts',
+ data: mergeConflictsStore.state,
+ components: {
+ 'diff-file-editor': gl.mergeConflicts.diffFileEditor,
+ 'inline-conflict-lines': gl.mergeConflicts.inlineConflictLines,
+ 'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines
+ },
+ computed: {
+ conflictsCountText() { return mergeConflictsStore.getConflictsCountText() },
+ readyToCommit() { return mergeConflictsStore.isReadyToCommit() },
+ commitButtonText() { return mergeConflictsStore.getCommitButtonText() },
+ showDiffViewTypeSwitcher() { return mergeConflictsStore.fileTextTypePresent() }
+ },
+ created() {
+ mergeConflictsService
+ .fetchConflictsData()
+ .done((data) => {
+ if (data.type === 'error') {
+ mergeConflictsStore.setFailedRequest(data.message);
+ } else {
+ mergeConflictsStore.setConflictsData(data);
+ }
+ })
+ .error(() => {
+ mergeConflictsStore.setFailedRequest();
+ })
+ .always(() => {
+ mergeConflictsStore.setLoadingState(false);
+
+ this.$nextTick(() => {
+ $('.js-syntax-highlight').syntaxHighlight();
+ });
+ });
+ },
+ methods: {
+ handleViewTypeChange(viewType) {
+ mergeConflictsStore.setViewType(viewType);
+ },
+ onClickResolveModeButton(file, mode) {
+ if (mode === INTERACTIVE_RESOLVE_MODE && file.resolveEditChanged) {
+ mergeConflictsStore.setPromptConfirmationState(file, true);
+ return;
+ }
+
+ mergeConflictsStore.setFileResolveMode(file, mode);
+ },
+ acceptDiscardConfirmation(file) {
+ mergeConflictsStore.setPromptConfirmationState(file, false);
+ mergeConflictsStore.setFileResolveMode(file, INTERACTIVE_RESOLVE_MODE);
+ },
+ cancelDiscardConfirmation(file) {
+ mergeConflictsStore.setPromptConfirmationState(file, false);
+ },
+ commit() {
+ mergeConflictsStore.setSubmitState(true);
+
+ mergeConflictsService
+ .submitResolveConflicts(mergeConflictsStore.getCommitData())
+ .done((data) => {
+ window.location.href = data.redirect_to;
+ })
+ .error(() => {
+ mergeConflictsStore.setSubmitState(false);
+ new Flash('Failed to save merge conflicts resolutions. Please try again!');
+ });
+ }
+ }
+ })
+});
diff --git a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6 b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6
new file mode 100644
index 00000000000..c8de586aa21
--- /dev/null
+++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6
@@ -0,0 +1,13 @@
+/* eslint-disable */
+((global) => {
+ global.mergeConflicts = global.mergeConflicts || {};
+
+ global.mergeConflicts.actions = {
+ methods: {
+ handleSelected(file, sectionId, selection) {
+ gl.mergeConflicts.mergeConflictsStore.handleSelected(file, sectionId, selection);
+ }
+ }
+ };
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6 b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6
new file mode 100644
index 00000000000..88c3a20ce13
--- /dev/null
+++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6
@@ -0,0 +1,19 @@
+/* eslint-disable */
+((global) => {
+ global.mergeConflicts = global.mergeConflicts || {};
+
+ global.mergeConflicts.utils = {
+ methods: {
+ lineCssClass(line) {
+ return {
+ 'head': line.isHead,
+ 'origin': line.isOrigin,
+ 'match': line.hasMatch,
+ 'selected': line.isSelected,
+ 'unselected': line.isUnselected
+ };
+ }
+ }
+ };
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index 05644b3d03c..a4b4db14db8 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-undef, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, padded-blocks, max-len */
/*= require jquery.waitforimages */
/*= require task_list */
@@ -36,13 +37,10 @@
};
MergeRequest.prototype.initTabs = function() {
- if (this.opts.action !== 'new') {
- // `MergeRequests#new` has no tab-persisting or lazy-loading behavior
- window.mrTabs = new MergeRequestTabs(this.opts);
- } else {
- // Show the first tab (Commits)
- return $('.merge-request-tabs a[data-toggle="tab"]:first').tab('show');
+ if (window.mrTabs) {
+ window.mrTabs.unbindEvents();
}
+ window.mrTabs = new MergeRequestTabs(this.opts);
};
MergeRequest.prototype.showAllCommits = function() {
@@ -99,7 +97,11 @@
return $.ajax({
type: 'PATCH',
url: $('form.js-issuable-update').attr('action'),
- data: patchData
+ data: patchData,
+ success: function(mergeRequest) {
+ document.querySelector('#task_status').innerText = mergeRequest.task_status;
+ document.querySelector('#task_status_short').innerText = mergeRequest.task_status_short;
+ }
});
// TODO (rspeicher): Make the merge request description inline-editable like a
// note so that we can re-use its form here
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index dcba4a8d275..b1928f8d279 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -1,9 +1,10 @@
+/* eslint-disable max-len, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, no-underscore-dangle, no-undef, one-var, one-var-declaration-per-line, quotes, comma-dangle, consistent-return, prefer-template, no-param-reassign, camelcase, vars-on-top, space-in-parens, curly, prefer-arrow-callback, no-unused-vars, no-return-assign, semi, object-shorthand, operator-assignment, padded-blocks, max-len */
// MergeRequestTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
// content on the MergeRequests#show page.
//
-/*= require jquery.cookie */
+/*= require js.cookie */
//
// ### Example Markup
@@ -56,9 +57,14 @@
MergeRequestTabs.prototype.commitsLoaded = false;
+ MergeRequestTabs.prototype.fixedLayoutPref = null;
+
function MergeRequestTabs(opts) {
this.opts = opts != null ? opts : {};
this.opts.setUrl = this.opts.setUrl !== undefined ? this.opts.setUrl : true;
+
+ this.buildsLoaded = this.opts.buildsLoaded || false;
+
this.setCurrentAction = bind(this.setCurrentAction, this);
this.tabShown = bind(this.tabShown, this);
this.showTab = bind(this.showTab, this);
@@ -66,11 +72,17 @@
this._location = location;
this.bindEvents();
this.activateTab(this.opts.action);
+ this.initAffix();
}
MergeRequestTabs.prototype.bindEvents = function() {
$(document).on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown);
- return $(document).on('click', '.js-show-tab', this.showTab);
+ $(document).on('click', '.js-show-tab', this.showTab);
+ };
+
+ MergeRequestTabs.prototype.unbindEvents = function() {
+ $(document).off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown);
+ $(document).off('click', '.js-show-tab', this.showTab);
};
MergeRequestTabs.prototype.showTab = function(event) {
@@ -85,11 +97,15 @@
if (action === 'commits') {
this.loadCommits($target.attr('href'));
this.expandView();
- } else if (action === 'diffs') {
+ this.resetViewContainer();
+ } else if (this.isDiffAction(action)) {
this.loadDiff($target.attr('href'));
if ((typeof bp !== "undefined" && bp !== null) && bp.getBreakpointSize() !== 'lg') {
this.shrinkView();
}
+ if (this.diffViewType() === 'parallel') {
+ this.expandViewContainer();
+ }
navBarHeight = $('.navbar-gitlab').outerHeight();
$.scrollTo(".merge-request-details .merge-request-tabs", {
offset: -navBarHeight
@@ -97,11 +113,14 @@
} else if (action === 'builds') {
this.loadBuilds($target.attr('href'));
this.expandView();
+ this.resetViewContainer();
} else if (action === 'pipelines') {
this.loadPipelines($target.attr('href'));
this.expandView();
+ this.resetViewContainer();
} else {
this.expandView();
+ this.resetViewContainer();
}
if (this.opts.setUrl) {
this.setCurrentAction(action);
@@ -111,7 +130,7 @@
MergeRequestTabs.prototype.scrollToElement = function(container) {
var $el, navBarHeight;
if (window.location.hash) {
- navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
+ navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight() + document.querySelector('.js-tabs-affix').offsetHeight;
$el = $(container + " " + window.location.hash + ":not(.match)");
if ($el.length) {
return $.scrollTo(container + " " + window.location.hash + ":not(.match)", {
@@ -126,7 +145,8 @@
if (action === 'show') {
action = 'notes';
}
- return $(".merge-request-tabs a[data-action='" + action + "']").tab('show');
+ // important note: the .tab('show') method triggers 'shown.bs.tab' event itself
+ $(".merge-request-tabs a[data-action='" + action + "']").tab('show');
};
// Replaces the current Merge Request-specific action in the URL with a new one
@@ -156,8 +176,9 @@
action = 'notes';
}
this.currentAction = action;
- // Remove a trailing '/commits' or '/diffs'
- new_state = this._location.pathname.replace(/\/(commits|diffs|builds|pipelines)(\.html)?\/?$/, '');
+ // Remove a trailing '/commits' '/diffs' '/builds' '/pipelines' '/new' '/new/diffs'
+ new_state = this._location.pathname.replace(/\/(commits|diffs|builds|pipelines|new|new\/diffs)(\.html)?\/?$/, '');
+
// Append the new action if we're on a tab other than 'notes'
if (action !== 'notes') {
new_state += "/" + action;
@@ -196,25 +217,33 @@
if (this.diffsLoaded) {
return;
}
+
+ // We extract pathname for the current Changes tab anchor href
+ // some pages like MergeRequestsController#new has query parameters on that anchor
+ var url = gl.utils.parseUrl(source);
+
return this._get({
- url: (source + ".json") + this._location.search,
+ url: (url.pathname + ".json") + this._location.search,
success: (function(_this) {
return function(data) {
$('#diffs').html(data.html);
- if (typeof DiffNotesApp !== 'undefined') {
- DiffNotesApp.compileComponents();
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
}
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'));
$('#diffs .js-syntax-highlight').syntaxHighlight();
$('#diffs .diff-file').singleFileDiff();
- if (_this.diffViewType() === 'parallel') {
+ if (_this.diffViewType() === 'parallel' && (_this.isDiffAction(_this.currentAction)) ) {
_this.expandViewContainer();
}
_this.diffsLoaded = true;
- _this.scrollToElement("#diffs");
- _this.highlighSelectedLine();
+ var anchoredDiff = gl.utils.getLocationHash();
+ if (anchoredDiff) _this.openAnchoredDiff(anchoredDiff, function() {
+ _this.scrollToElement("#diffs");
+ _this.highlighSelectedLine();
+ });
_this.filesCommentButton = $('.files .diff-file').filesCommentButton();
return $(document).off('click', '.diff-line-num a').on('click', '.diff-line-num a', function(e) {
e.preventDefault();
@@ -227,15 +256,26 @@
});
};
+ MergeRequestTabs.prototype.openAnchoredDiff = function(anchoredDiff, cb) {
+ var diffTitle = $('#file-path-' + anchoredDiff);
+ var diffFile = diffTitle.closest('.diff-file');
+ var nothingHereBlock = $('.nothing-here-block:visible', diffFile);
+ if (nothingHereBlock.length) {
+ diffFile.singleFileDiff(true, cb);
+ } else {
+ cb();
+ }
+ };
+
MergeRequestTabs.prototype.highlighSelectedLine = function() {
var $diffLine, diffLineTop, hashClassString, locationHash, navBarHeight;
$('.hll').removeClass('hll');
locationHash = window.location.hash;
if (locationHash !== '') {
- hashClassString = "." + (locationHash.replace('#', ''));
+ dataLineString = '[data-line-code="' + locationHash.replace('#', '') + '"]';
$diffLine = $(locationHash + ":not(.match)", $('#diffs'));
if (!$diffLine.is('tr')) {
- $diffLine = $('#diffs').find("td" + locationHash + ", td" + hashClassString);
+ $diffLine = $('#diffs').find("td" + locationHash + ", td" + dataLineString);
} else {
$diffLine = $diffLine.find('td');
}
@@ -258,6 +298,7 @@
document.querySelector("div#builds").innerHTML = data.html;
gl.utils.localTimeAgo($('.js-timeago', 'div#builds'));
_this.buildsLoaded = true;
+ if (!this.pipelines) this.pipelines = new gl.Pipelines();
return _this.scrollToElement("#builds");
};
})(this)
@@ -308,11 +349,25 @@
MergeRequestTabs.prototype.diffViewType = function() {
return $('.inline-parallel-buttons a.active').data('view-type');
- // Returns diff view type
+ };
+
+ MergeRequestTabs.prototype.isDiffAction = function(action) {
+ return action === 'diffs' || action === 'new/diffs'
};
MergeRequestTabs.prototype.expandViewContainer = function() {
- return $('.container-fluid').removeClass('container-limited');
+ var $wrapper = $('.content-wrapper .container-fluid');
+ if (this.fixedLayoutPref === null) {
+ this.fixedLayoutPref = $wrapper.hasClass('container-limited');
+ }
+ $wrapper.removeClass('container-limited');
+ };
+
+ MergeRequestTabs.prototype.resetViewContainer = function() {
+ if (this.fixedLayoutPref !== null) {
+ $('.content-wrapper .container-fluid')
+ .toggleClass('container-limited', this.fixedLayoutPref);
+ }
};
MergeRequestTabs.prototype.shrinkView = function() {
@@ -329,7 +384,7 @@
MergeRequestTabs.prototype.expandView = function() {
var $gutterIcon;
- if ($.cookie('collapsed_gutter') === 'true') {
+ if (Cookies.get('collapsed_gutter') === 'true') {
return;
}
$gutterIcon = $('.js-sidebar-toggle i:visible');
@@ -343,6 +398,43 @@
// Only when sidebar is collapsed
};
+ MergeRequestTabs.prototype.initAffix = function () {
+ var $tabs = $('.js-tabs-affix');
+
+ // Screen space on small screens is usually very sparse
+ // So we dont affix the tabs on these
+ if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
+
+ var $diffTabs = $('#diff-notes-app'),
+ $fixedNav = $('.navbar-fixed-top'),
+ $layoutNav = $('.layout-nav');
+
+ $tabs.off('affix.bs.affix affix-top.bs.affix')
+ .affix({
+ offset: {
+ top: function () {
+ var tabsTop = $diffTabs.offset().top - $tabs.height();
+ tabsTop = tabsTop - ($fixedNav.height() + $layoutNav.height());
+
+ return tabsTop;
+ }
+ }
+ }).on('affix.bs.affix', function () {
+ $diffTabs.css({
+ marginTop: $tabs.height()
+ });
+ }).on('affix-top.bs.affix', function () {
+ $diffTabs.css({
+ marginTop: ''
+ });
+ });
+
+ // Fix bug when reloading the page already scrolling
+ if ($tabs.hasClass('affix')) {
+ $tabs.trigger('affix.bs.affix');
+ }
+ };
+
return MergeRequestTabs;
})();
diff --git a/app/assets/javascripts/merge_request_widget.js b/app/assets/javascripts/merge_request_widget.js.es6
index 7bbcdf59838..a55fe9df0b3 100644
--- a/app/assets/javascripts/merge_request_widget.js
+++ b/app/assets/javascripts/merge_request_widget.js.es6
@@ -1,7 +1,33 @@
-(function() {
+/* eslint-disable */
+ ((global) => {
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
- this.MergeRequestWidget = (function() {
+ const DEPLOYMENT_TEMPLATE = `<div class="mr-widget-heading" id="<%- id %>">
+ <div class="ci_widget ci-success">
+ <%= ci_success_icon %>
+ <span>
+ Deployed to
+ <a href="<%- url %>" target="_blank" class="environment">
+ <%- name %>
+ </a>
+ <span class="js-environment-timeago" data-toggle="tooltip" data-placement="top" data-title="<%- deployed_at_formatted %>">
+ <%- deployed_at %>
+ </span>
+ <a class="js-environment-link" href="<%- external_url %>" target="_blank">
+ <i class="fa fa-external-link"></i>
+ View on <%- external_url_formatted %>
+ </a>
+ </span>
+ <span class="stop-env-container js-stop-env-link">
+ <a href="<%- stop_url %>" class="close-evn-link" data-method="post" rel="nofollow" data-confirm="Are you sure you want to stop this environment?">
+ <i class="fa fa-stop-circle-o"/>
+ Stop environment
+ </a>
+ </span>
+ </div>
+ </div>`;
+
+ global.MergeRequestWidget = (function() {
function MergeRequestWidget(opts) {
// Initialize MergeRequestWidget behavior
//
@@ -10,17 +36,23 @@
// ci_status_url - String, URL to use to check CI status
//
this.opts = opts;
+ this.$widgetBody = $('.mr-widget-body');
$('#modal_merge_info').modal({
show: false
});
this.firstCICheck = true;
this.readyForCICheck = false;
+ this.readyForCIEnvironmentCheck = false;
this.cancel = false;
clearInterval(this.fetchBuildStatusInterval);
+ clearInterval(this.fetchBuildEnvironmentStatusInterval);
this.clearEventListeners();
this.addEventListeners();
this.getCIStatus(false);
+ this.getCIEnvironmentsStatus();
+ this.retrieveSuccessIcon();
this.pollCIStatus();
+ this.pollCIEnvironmentsStatus();
notifyPermissions();
}
@@ -35,12 +67,13 @@
MergeRequestWidget.prototype.addEventListeners = function() {
var allowedPages;
allowedPages = ['show', 'commits', 'builds', 'pipelines', 'changes'];
- return $(document).on('page:change.merge_request', (function(_this) {
+ $(document).on('page:change.merge_request', (function(_this) {
return function() {
var page;
page = $('body').data('page').split(':').last();
if (allowedPages.indexOf(page) < 0) {
clearInterval(_this.fetchBuildStatusInterval);
+ clearInterval(_this.fetchBuildEnvironmentStatusInterval);
_this.cancelPolling();
return _this.clearEventListeners();
}
@@ -48,6 +81,12 @@
})(this));
};
+ MergeRequestWidget.prototype.retrieveSuccessIcon = function() {
+ const $ciSuccessIcon = $('.js-success-icon');
+ this.$ciSuccessIcon = $ciSuccessIcon.html();
+ $ciSuccessIcon.remove();
+ }
+
MergeRequestWidget.prototype.mergeInProgress = function(deleteSourceBranch) {
if (deleteSourceBranch == null) {
deleteSourceBranch = false;
@@ -62,7 +101,7 @@
urlSuffix = deleteSourceBranch ? '?deleted_source_branch=true' : '';
return window.location.href = window.location.pathname + urlSuffix;
} else if (data.merge_error) {
- return $('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>");
+ return this.$widgetBody.html("<h4>" + data.merge_error + "</h4>");
} else {
callback = function() {
return merge_request_widget.mergeInProgress(deleteSourceBranch);
@@ -118,6 +157,7 @@
if (data.status === '') {
return;
}
+ if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
if (_this.firstCICheck || data.status !== _this.opts.ci_status && (data.status != null)) {
_this.opts.ci_status = data.status;
_this.showCIStatus(data.status);
@@ -150,6 +190,46 @@
})(this));
};
+ MergeRequestWidget.prototype.pollCIEnvironmentsStatus = function() {
+ this.fetchBuildEnvironmentStatusInterval = setInterval(() => {
+ if (!this.readyForCIEnvironmentCheck) return;
+ this.getCIEnvironmentsStatus();
+ this.readyForCIEnvironmentCheck = false;
+ }, 300000);
+ };
+
+ MergeRequestWidget.prototype.getCIEnvironmentsStatus = function() {
+ $.getJSON(this.opts.ci_environments_status_url, (environments) => {
+ if (this.cancel) return;
+ this.readyForCIEnvironmentCheck = true;
+ if (environments && environments.length) this.renderEnvironments(environments);
+ });
+ };
+
+ MergeRequestWidget.prototype.renderEnvironments = function(environments) {
+ for (let i = 0; i < environments.length; i++) {
+ const environment = environments[i];
+ if ($(`.mr-state-widget #${ environment.id }`).length) return;
+ const $template = $(DEPLOYMENT_TEMPLATE);
+ if (!environment.external_url || !environment.external_url_formatted) $('.js-environment-link', $template).remove();
+
+ if (!environment.stop_url) {
+ $('.js-stop-env-link', $template).remove();
+ }
+
+ if (environment.deployed_at && environment.deployed_at_formatted) {
+ environment.deployed_at = gl.utils.getTimeago().format(environment.deployed_at, 'gl_en') + '.';
+ } else {
+ $('.js-environment-timeago', $template).remove();
+ environment.name += '.';
+ }
+ environment.ci_success_icon = this.$ciSuccessIcon;
+ const templateString = _.unescape($template[0].outerHTML);
+ const template = _.template(templateString)(environment)
+ this.$widgetBody.before(template);
+ }
+ };
+
MergeRequestWidget.prototype.showCIStatus = function(state) {
var allowed_states;
if (state == null) {
@@ -165,7 +245,7 @@
case "not_found":
return this.setMergeButtonClass('btn-danger');
case "running":
- return this.setMergeButtonClass('btn-warning');
+ return this.setMergeButtonClass('btn-info');
case "success":
case "success_with_warnings":
return this.setMergeButtonClass('btn-create');
@@ -183,11 +263,11 @@
};
MergeRequestWidget.prototype.setMergeButtonClass = function(css_class) {
- return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-warning btn-create').addClass(css_class);
+ return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class);
};
return MergeRequestWidget;
})();
-}).call(this);
+ })(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merged_buttons.js b/app/assets/javascripts/merged_buttons.js
index 1fed38661a2..15a12c3d985 100644
--- a/app/assets/javascripts/merged_buttons.js
+++ b/app/assets/javascripts/merged_buttons.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-undef, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index bc1a99057d9..db7561a3a75 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-use-before-define, camelcase, quotes, object-shorthand, no-shadow, no-unused-vars, no-undef, comma-dangle, no-var, prefer-template, no-underscore-dangle, consistent-return, one-var, one-var-declaration-per-line, default-case, prefer-arrow-callback, padded-blocks, max-len */
(function() {
this.Milestone = (function() {
Milestone.updateIssue = function(li, issue_url, data) {
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index c8031174dd2..67796083790 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-undef, no-param-reassign, no-shadow, padded-blocks, max-len */
(function() {
this.MilestoneSelect = (function() {
function MilestoneSelect(currentProject) {
@@ -7,7 +8,7 @@
this.currentProject = JSON.parse(currentProject);
}
$('.js-milestone-select').each(function(i, dropdown) {
- var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, showAny, showNo, showUpcoming, useId;
+ var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, showAny, showNo, showUpcoming, useId, showMenuAbove;
$dropdown = $(dropdown);
projectId = $dropdown.data('project-id');
milestonesUrl = $dropdown.data('milestones');
@@ -15,6 +16,7 @@
selectedMilestone = $dropdown.data('selected');
showNo = $dropdown.data('show-no');
showAny = $dropdown.data('show-any');
+ showMenuAbove = $dropdown.data('showMenuAbove');
showUpcoming = $dropdown.data('show-upcoming');
useId = $dropdown.data('use-id');
defaultLabel = $dropdown.data('default-label');
@@ -31,12 +33,12 @@
collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left"> <%- title %> </span>');
}
return $dropdown.glDropdown({
+ showMenuAbove: showMenuAbove,
data: function(term, callback) {
return $.ajax({
url: milestonesUrl
}).done(function(data) {
- var extraOptions;
- extraOptions = [];
+ var extraOptions = [];
if (showAny) {
extraOptions.push({
id: 0,
@@ -58,10 +60,14 @@
title: 'Upcoming'
});
}
- if (extraOptions.length > 2) {
+ if (extraOptions.length) {
extraOptions.push('divider');
}
- return callback(extraOptions.concat(data));
+
+ callback(extraOptions.concat(data));
+ if (showMenuAbove) {
+ $dropdown.data('glDropdown').positionMenuAbove();
+ }
});
},
filterable: true,
@@ -69,19 +75,20 @@
fields: ['title']
},
selectable: true,
- toggleLabel: function(selected) {
- if (selected && 'id' in selected) {
+ toggleLabel: function(selected, el, e) {
+ if (selected && 'id' in selected && $(el).hasClass('is-active')) {
return selected.title;
} else {
return defaultLabel;
}
},
+ defaultLabel: defaultLabel,
fieldName: $dropdown.data('field-name'),
text: function(milestone) {
return _.escape(milestone.title);
},
id: function(milestone) {
- if (!useId) {
+ if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) {
return milestone.name;
} else {
return milestone.id;
@@ -95,15 +102,17 @@
// display:block overrides the hide-collapse rule
return $value.css('display', '');
},
+ vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(selected, $el, e) {
var data, isIssueIndex, isMRIndex, page;
page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index';
isMRIndex = (page === page && page === 'projects:merge_requests:index');
- if ($dropdown.hasClass('js-filter-bulk-update')) {
+ if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
+ e.preventDefault();
return;
}
- if (page === 'projects:boards:show') {
+ if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar')) {
gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = selected.name;
gl.issueBoards.BoardsStore.updateFiltersUrl();
e.preventDefault();
@@ -116,6 +125,24 @@
return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
+ } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ if (selected.id !== -1) {
+ Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'milestone', new ListMilestone({
+ id: selected.id,
+ title: selected.name
+ }));
+ } else {
+ Vue.delete(gl.issueBoards.BoardsStore.detail.issue, 'milestone');
+ }
+
+ $dropdown.trigger('loading.gl.dropdown');
+ $loading.fadeIn();
+
+ gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
+ .then(function () {
+ $dropdown.trigger('loaded.gl.dropdown');
+ $loading.fadeOut();
+ });
} else {
selected = $selectbox.find('input[type="hidden"]').val();
data = {};
@@ -135,7 +162,7 @@
if (data.milestone != null) {
data.milestone.namespace = _this.currentProject.namespace;
data.milestone.path = _this.currentProject.path;
- data.milestone.remaining = $.timefor(data.milestone.due_date);
+ data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date);
$value.html(milestoneLinkTemplate(data.milestone));
return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone));
} else {
diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js
index 10f4fd106d8..87c903ec576 100644
--- a/app/assets/javascripts/namespace_select.js
+++ b/app/assets/javascripts/namespace_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, vars-on-top, one-var-declaration-per-line, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, no-undef, prefer-arrow-callback, padded-blocks, no-param-reassign, no-cond-assign, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/network/branch-graph.js b/app/assets/javascripts/network/branch_graph.js
index 91132af273a..e3dc599b90a 100644
--- a/app/assets/javascripts/network/branch-graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, comma-dangle, one-var, one-var-declaration-per-line, no-mixed-operators, new-cap, no-undef, no-plusplus, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/network/network.js b/app/assets/javascripts/network/network.js
index 7baebcd100a..5a8f723a27b 100644
--- a/app/assets/javascripts/network/network.js
+++ b/app/assets/javascripts/network/network.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, no-undef, quote-props, prefer-template, comma-dangle, padded-blocks, max-len */
(function() {
this.Network = (function() {
function Network(opts) {
diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js
index 67c3e645364..732d92845cb 100644
--- a/app/assets/javascripts/network/network_bundle.js
+++ b/app/assets/javascripts/network/network_bundle.js
@@ -1,5 +1,6 @@
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, quotes, no-var, vars-on-top, camelcase, no-undef, comma-dangle, consistent-return, padded-blocks, max-len */
// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
@@ -8,6 +9,8 @@
(function() {
$(function() {
+ if (!$(".network-graph").length) return;
+
var network_graph;
network_graph = new Network({
url: $(".network-graph").attr('data-url'),
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index 20aa2fced27..29a323dd4c6 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, one-var, space-before-blocks, prefer-rest-params, max-len, vars-on-top, no-plusplus, 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, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js
index 21bf8867f7b..8fb8f3e4a5f 100644
--- a/app/assets/javascripts/new_commit_form.js
+++ b/app/assets/javascripts/new_commit_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-return-assign, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index c6854f703fb..a84c514dac7 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
/*= require autosave */
/*= require autosize */
@@ -11,7 +12,7 @@
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.Notes = (function() {
- var isMetaKey;
+ const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
Notes.interval = null;
@@ -32,6 +33,7 @@
this.resetMainTargetForm = bind(this.resetMainTargetForm, this);
this.refresh = bind(this.refresh, this);
this.keydownNoteText = bind(this.keydownNoteText, this);
+ this.toggleCommitList = bind(this.toggleCommitList, this);
this.notes_url = notes_url;
this.note_ids = note_ids;
this.last_fetched_at = last_fetched_at;
@@ -45,6 +47,7 @@
this.setPollingInterval();
this.setupMainTargetNoteForm();
this.initTaskList();
+ this.collapseLongCommitList();
}
Notes.prototype.addBinding = function() {
@@ -80,10 +83,13 @@
$(document).on("click", ".js-add-diff-note-button", this.addDiffNote);
// hide diff note form
$(document).on("click", ".js-close-discussion-note-form", this.cancelDiscussionForm);
+ // toggle commit list
+ $(document).on("click", '.system-note-commit-list-toggler', this.toggleCommitList);
// fetch notes when tab becomes visible
$(document).on("visibilitychange", this.visibilityChange);
// when issue status changes, we need to refresh data
$(document).on("issuable:change", this.refresh);
+
// when a key is clicked on the notes
return $(document).on("keydown", ".js-note-text", this.keydownNoteText);
};
@@ -107,15 +113,17 @@
$(document).off("click", ".js-note-discard");
$(document).off("keydown", ".js-note-text");
$(document).off('click', '.js-comment-resolve-button');
+ $(document).off("click", '.system-note-commit-list-toggler');
$('.note .js-task-list-container').taskList('disable');
return $(document).off('tasklist:changed', '.note .js-task-list-container');
};
Notes.prototype.keydownNoteText = function(e) {
var $textarea, discussionNoteForm, editNote, myLastNote, myLastNoteEditBtn, newText, originalText;
- if (isMetaKey(e)) {
+ if (gl.utils.isMetaKey(e)) {
return;
}
+
$textarea = $(e.target);
// Edit previous note when UP arrow is hit
switch (e.which) {
@@ -155,10 +163,6 @@
}
};
- isMetaKey = function(e) {
- return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
- };
-
Notes.prototype.initRefresh = function() {
clearInterval(Notes.interval);
return Notes.interval = setInterval((function(_this) {
@@ -262,6 +266,7 @@
$notesList.append(note.html).syntaxHighlight();
// Update datetime format on the recent note
gl.utils.localTimeAgo($notesList.find("#note_" + note.id + " .js-timeago"), false);
+ this.collapseLongCommitList();
this.initTaskList();
this.refresh();
return this.updateNotesCount(1);
@@ -324,8 +329,8 @@
discussionContainer.append(note_html);
}
- if (typeof DiffNotesApp !== 'undefined') {
- DiffNotesApp.compileComponents();
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
}
gl.utils.localTimeAgo($('.js-timeago', note_html), false);
@@ -432,14 +437,12 @@
var $form = $(xhr.target);
if ($form.attr('data-resolve-all') != null) {
- var namespacePath = $form.attr('data-namespace-path'),
- projectPath = $form.attr('data-project-path')
- discussionId = $form.attr('data-discussion-id'),
- mergeRequestId = $form.attr('data-noteable-iid'),
- namespace = namespacePath + '/' + projectPath;
+ var projectPath = $form.data('project-path');
+ var discussionId = $form.data('discussion-id');
+ var mergeRequestId = $form.data('noteable-iid');
if (ResolveService != null) {
- ResolveService.toggleResolveForDiscussion(namespace, mergeRequestId, discussionId);
+ ResolveService.toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId);
}
}
@@ -467,8 +470,8 @@
$note_li.replaceWith($html);
- if (typeof DiffNotesApp !== 'undefined') {
- DiffNotesApp.compileComponents();
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
}
};
@@ -560,11 +563,9 @@
note = $(el);
notes = note.closest(".notes");
- if (typeof DiffNotesApp !== "undefined" && DiffNotesApp !== null) {
- ref = DiffNotesApp.$refs[noteId];
-
- if (ref) {
- ref.$destroy(true);
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ if (gl.diffNoteApps[noteId]) {
+ gl.diffNoteApps[noteId].$destroy();
}
}
@@ -644,11 +645,12 @@
form.find('.js-note-target-close').remove();
this.setupNoteForm(form);
- if (typeof DiffNotesApp !== 'undefined') {
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
var $commentBtn = form.find('comment-and-resolve-btn');
$commentBtn
.attr(':discussion-id', "'" + dataHolder.data('discussionId') + "'");
- DiffNotesApp.$compile($commentBtn.get(0));
+
+ gl.diffNotesCompileComponents();
}
form.find(".js-note-text").focus();
@@ -846,18 +848,47 @@
return this.notesCountBadge.text(parseInt(this.notesCountBadge.text()) + updateCount);
};
- Notes.prototype.resolveDiscussion = function () {
- var $this = $(this),
- discussionId = $this.attr('data-discussion-id');
+ Notes.prototype.resolveDiscussion = function() {
+ var $this = $(this);
+ var discussionId = $this.attr('data-discussion-id');
$this
.closest('form')
.attr('data-discussion-id', discussionId)
.attr('data-resolve-all', 'true')
- .attr('data-namespace-path', $this.attr('data-namespace-path'))
.attr('data-project-path', $this.attr('data-project-path'));
};
+ Notes.prototype.toggleCommitList = function(e) {
+ const $element = $(e.target);
+ const $closestSystemCommitList = $element.siblings('.system-note-commit-list');
+
+ $closestSystemCommitList.toggleClass('hide-shade');
+ };
+
+ /**
+ Scans system notes with `ul` elements in system note body
+ then collapse long commit list pushed by user to make it less
+ intrusive.
+ */
+ Notes.prototype.collapseLongCommitList = function() {
+ const systemNotes = $('#notes-list').find('li.system-note').has('ul');
+
+ $.each(systemNotes, function(index, systemNote) {
+ const $systemNote = $(systemNote);
+ const headerMessage = $systemNote.find('.note-text').find('p:first').text().replace(':', '');
+
+ $systemNote.find('.note-header .system-note-message').html(headerMessage);
+
+ if ($systemNote.find('li').length > MAX_VISIBLE_COMMIT_LIST_COUNT) {
+ $systemNote.find('.note-text').addClass('system-note-commit-list');
+ $systemNote.find('.system-note-commit-list-toggler').show();
+ } else {
+ $systemNote.find('.note-text').addClass('system-note-commit-list hide-shade');
+ }
+ });
+ };
+
return Notes;
})();
diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js
index a41e9d3fabe..b152d26733f 100644
--- a/app/assets/javascripts/notifications_dropdown.js
+++ b/app/assets/javascripts/notifications_dropdown.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, prefer-arrow-callback, no-else-return, no-undef, padded-blocks, max-len */
(function() {
this.NotificationsDropdown = (function() {
function NotificationsDropdown() {
diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js
index 6b2ef17ef6b..2034f9a748a 100644
--- a/app/assets/javascripts/notifications_form.js
+++ b/app/assets/javascripts/notifications_form.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, newline-per-chained-call, comma-dangle, consistent-return, prefer-arrow-callback, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js
deleted file mode 100644
index b81ed50cb48..00000000000
--- a/app/assets/javascripts/pager.js
+++ /dev/null
@@ -1,63 +0,0 @@
-(function() {
- this.Pager = {
- init: function(limit, preload, disable, callback) {
- this.limit = limit != null ? limit : 0;
- this.disable = disable != null ? disable : false;
- this.callback = callback != null ? callback : $.noop;
- this.loading = $('.loading').first();
- if (preload) {
- this.offset = 0;
- this.getOld();
- } else {
- this.offset = this.limit;
- }
- return this.initLoadMore();
- },
- getOld: function() {
- this.loading.show();
- return $.ajax({
- type: "GET",
- url: $(".content_list").data('href') || location.href,
- data: "limit=" + this.limit + "&offset=" + this.offset,
- complete: (function(_this) {
- return function() {
- return _this.loading.hide();
- };
- })(this),
- success: function(data) {
- Pager.append(data.count, data.html);
- return Pager.callback();
- },
- dataType: "json"
- });
- },
- append: function(count, html) {
- $(".content_list").append(html);
- if (count > 0) {
- return this.offset += count;
- } else {
- return this.disable = true;
- }
- },
- initLoadMore: function() {
- $(document).unbind('scroll');
- return $(document).endlessScroll({
- bottomPixels: 400,
- fireDelay: 1000,
- fireOnce: true,
- ceaseFire: function() {
- return Pager.disable;
- },
- callback: (function(_this) {
- return function(i) {
- if (!_this.loading.is(':visible')) {
- _this.loading.show();
- return Pager.getOld();
- }
- };
- })(this)
- });
- }
- };
-
-}).call(this);
diff --git a/app/assets/javascripts/pager.js.es6 b/app/assets/javascripts/pager.js.es6
new file mode 100644
index 00000000000..e35cf6d295e
--- /dev/null
+++ b/app/assets/javascripts/pager.js.es6
@@ -0,0 +1,73 @@
+(() => {
+ const ENDLESS_SCROLL_BOTTOM_PX = 400;
+ const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000;
+
+ const Pager = {
+ init(limit = 0, preload = false, disable = false, callback = $.noop) {
+ this.limit = limit;
+ this.offset = this.limit;
+ this.disable = disable;
+ this.callback = callback;
+ this.loading = $('.loading').first();
+ if (preload) {
+ this.offset = 0;
+ this.getOld();
+ }
+ this.initLoadMore();
+ },
+
+ getOld() {
+ this.loading.show();
+ $.ajax({
+ type: 'GET',
+ url: $('.content_list').data('href') || window.location.href,
+ data: `limit=${this.limit}&offset=${this.offset}`,
+ dataType: 'json',
+ error: () => this.loading.hide(),
+ success: (data) => {
+ this.append(data.count, data.html);
+ this.callback();
+
+ // keep loading until we've filled the viewport height
+ if (!this.disable && !this.isScrollable()) {
+ this.getOld();
+ } else {
+ this.loading.hide();
+ }
+ },
+ });
+ },
+
+ append(count, html) {
+ $('.content_list').append(html);
+ if (count > 0) {
+ this.offset += count;
+ } else {
+ this.disable = true;
+ }
+ },
+
+ isScrollable() {
+ const $w = $(window);
+ return $(document).height() > $w.height() + $w.scrollTop() + ENDLESS_SCROLL_BOTTOM_PX;
+ },
+
+ initLoadMore() {
+ $(document).unbind('scroll');
+ $(document).endlessScroll({
+ bottomPixels: ENDLESS_SCROLL_BOTTOM_PX,
+ fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS,
+ fireOnce: true,
+ ceaseFire: () => this.disable === true,
+ callback: () => {
+ if (!this.loading.is(':visible')) {
+ this.loading.show();
+ this.getOld();
+ }
+ },
+ });
+ },
+ };
+
+ window.Pager = Pager;
+})();
diff --git a/app/assets/javascripts/pipeline.js.es6 b/app/assets/javascripts/pipeline.js.es6
deleted file mode 100644
index bf33eb10100..00000000000
--- a/app/assets/javascripts/pipeline.js.es6
+++ /dev/null
@@ -1,15 +0,0 @@
-(function() {
- function toggleGraph() {
- const $pipelineBtn = $(this).closest('.toggle-pipeline-btn');
- const $pipelineGraph = $(this).closest('.row-content-block').next('.pipeline-graph');
- const $btnText = $(this).find('.toggle-btn-text');
-
- $($pipelineBtn).add($pipelineGraph).toggleClass('graph-collapsed');
-
- const graphCollapsed = $pipelineGraph.hasClass('graph-collapsed');
-
- graphCollapsed ? $btnText.text('Expand') : $btnText.text('Hide')
- }
-
- $(document).on('click', '.toggle-pipeline-btn', toggleGraph);
-})();
diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6
new file mode 100644
index 00000000000..a84db9c0233
--- /dev/null
+++ b/app/assets/javascripts/pipelines.js.es6
@@ -0,0 +1,30 @@
+/* eslint-disable */
+((global) => {
+
+ class Pipelines {
+ constructor() {
+ this.addMarginToBuildColumns();
+ }
+
+ addMarginToBuildColumns() {
+ this.pipelineGraph = document.querySelector('.pipeline-graph');
+ const secondChildBuildNodes = document.querySelector('.pipeline-graph').querySelectorAll('.build:nth-child(2)');
+ for (buildNodeIndex in secondChildBuildNodes) {
+ const buildNode = secondChildBuildNodes[buildNodeIndex];
+ const firstChildBuildNode = buildNode.previousElementSibling;
+ if (!firstChildBuildNode || !firstChildBuildNode.matches('.build')) continue;
+ const multiBuildColumn = buildNode.closest('.stage-column');
+ const previousColumn = multiBuildColumn.previousElementSibling;
+ if (!previousColumn || !previousColumn.matches('.stage-column')) continue;
+ multiBuildColumn.classList.add('left-margin');
+ firstChildBuildNode.classList.add('left-connector');
+ const columnBuilds = previousColumn.querySelectorAll('.build');
+ if (columnBuilds.length === 1) previousColumn.classList.add('no-margin');
+ }
+ this.pipelineGraph.classList.remove('hidden');
+ }
+ }
+
+ global.Pipelines = Pipelines;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 5200487814f..3723aa24942 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, wrap-iife, no-else-return, consistent-return, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, no-undef, camelcase, prefer-arrow-callback, max-len */
// MarkdownPreview
//
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js.es6
index 30cd6f6e470..6da6c1d0295 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js.es6
@@ -1,47 +1,46 @@
-(function() {
- var GitLabCrop,
- bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+/* eslint-disable */
+((global) => {
- GitLabCrop = (function() {
- var FILENAMEREGEX;
+ // Matches everything but the file name
+ const FILENAMEREGEX = /^.*[\\\/]/;
- // Matches everything but the file name
- FILENAMEREGEX = /^.*[\\\/]/;
+ class GitLabCrop {
+ constructor(input, { filename, previewImage, modalCrop, pickImageEl, uploadImageBtn, modalCropImg,
+ exportWidth = 200, exportHeight = 200, cropBoxWidth = 200, cropBoxHeight = 200 } = {}) {
- function GitLabCrop(input, opts) {
- var ref, ref1, ref2, ref3, ref4;
- if (opts == null) {
- opts = {};
- }
- this.onUploadImageBtnClick = bind(this.onUploadImageBtnClick, this);
- this.onModalHide = bind(this.onModalHide, this);
- this.onModalShow = bind(this.onModalShow, this);
- this.onPickImageClick = bind(this.onPickImageClick, this);
+ 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);
- // We should rename to avoid spec to fail
- // Form will submit the proper input filed with a file using FormData
- this.fileInput.attr('name', (this.fileInput.attr('name')) + "-trigger").attr('id', (this.fileInput.attr('id')) + "-trigger");
- // Set defaults
- this.exportWidth = (ref = opts.exportWidth) != null ? ref : 200, this.exportHeight = (ref1 = opts.exportHeight) != null ? ref1 : 200, this.cropBoxWidth = (ref2 = opts.cropBoxWidth) != null ? ref2 : 200, this.cropBoxHeight = (ref3 = opts.cropBoxHeight) != null ? ref3 : 200, this.form = (ref4 = opts.form) != null ? ref4 : this.fileInput.parents('form'), this.filename = opts.filename, this.previewImage = opts.previewImage, this.modalCrop = opts.modalCrop, this.pickImageEl = opts.pickImageEl, this.uploadImageBtn = opts.uploadImageBtn, this.modalCropImg = opts.modalCropImg;
- // Required params
- // Ensure needed elements are jquery objects
- // If selector is provided we will convert them to a jQuery Object
- this.filename = this.getElement(this.filename);
- this.previewImage = this.getElement(this.previewImage);
- this.pickImageEl = this.getElement(this.pickImageEl);
- // Modal elements usually are outside the @form element
- this.modalCrop = _.isString(this.modalCrop) ? $(this.modalCrop) : this.modalCrop;
- this.uploadImageBtn = _.isString(this.uploadImageBtn) ? $(this.uploadImageBtn) : this.uploadImageBtn;
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.exportWidth = exportWidth;
+ this.exportHeight = exportHeight;
+ this.cropBoxWidth = cropBoxWidth;
+ this.cropBoxHeight = cropBoxHeight;
+ this.form = this.fileInput.parents('form');
+ this.filename = filename;
+ this.previewImage = previewImage;
+ this.modalCrop = modalCrop;
+ this.pickImageEl = pickImageEl;
+ this.uploadImageBtn = uploadImageBtn;
+ this.modalCropImg = modalCropImg;
+ this.filename = this.getElement(filename);
+ this.previewImage = this.getElement(previewImage);
+ this.pickImageEl = this.getElement(pickImageEl);
+ this.modalCrop = _.isString(modalCrop) ? $(modalCrop) : modalCrop;
+ this.uploadImageBtn = _.isString(uploadImageBtn) ? $(uploadImageBtn) : uploadImageBtn;
+ this.modalCropImg = _.isString(modalCropImg) ? $(modalCropImg) : modalCropImg;
this.cropActionsBtn = this.modalCrop.find('[data-method]');
this.bindEvents();
}
- GitLabCrop.prototype.getElement = function(selector) {
+ getElement(selector) {
return $(selector, this.form);
- };
+ }
- GitLabCrop.prototype.bindEvents = function() {
+ bindEvents() {
var _this;
_this = this;
this.fileInput.on('change', function(e) {
@@ -57,13 +56,13 @@
return _this.onActionBtnClick(btn);
});
return this.croppedImageBlob = null;
- };
+ }
- GitLabCrop.prototype.onPickImageClick = function() {
+ onPickImageClick() {
return this.fileInput.trigger('click');
- };
+ }
- GitLabCrop.prototype.onModalShow = function() {
+ onModalShow() {
var _this;
_this = this;
return this.modalCropImg.cropper({
@@ -95,44 +94,44 @@
});
}
});
- };
+ }
- GitLabCrop.prototype.onModalHide = function() {
+ onModalHide() {
return this.modalCropImg.attr('src', '').cropper('destroy');
- };
+ }
- GitLabCrop.prototype.onUploadImageBtnClick = function(e) { // Remove attached image
- e.preventDefault(); // Destroy cropper instance
+ onUploadImageBtnClick(e) {
+ e.preventDefault();
this.setBlob();
this.setPreview();
this.modalCrop.modal('hide');
return this.fileInput.val('');
- };
+ }
- GitLabCrop.prototype.onActionBtnClick = function(btn) {
+ onActionBtnClick(btn) {
var data, result;
data = $(btn).data();
if (this.modalCropImg.data('cropper') && data.method) {
return result = this.modalCropImg.cropper(data.method, data.option);
}
- };
+ }
- GitLabCrop.prototype.onFileInputChange = function(e, input) {
+ onFileInputChange(e, input) {
return this.readFile(input);
- };
+ }
- GitLabCrop.prototype.readFile = function(input) {
+ readFile(input) {
var _this, reader;
_this = this;
reader = new FileReader;
- reader.onload = function() {
+ reader.onload = () => {
_this.modalCropImg.attr('src', reader.result);
return _this.modalCrop.modal('show');
};
return reader.readAsDataURL(input.files[0]);
- };
+ }
- GitLabCrop.prototype.dataURLtoBlob = function(dataURL) {
+ dataURLtoBlob(dataURL) {
var array, binary, i, k, len, v;
binary = atob(dataURL.split(',')[1]);
array = [];
@@ -143,35 +142,32 @@
return new Blob([new Uint8Array(array)], {
type: 'image/png'
});
- };
+ }
- GitLabCrop.prototype.setPreview = function() {
+ setPreview() {
var filename;
this.previewImage.attr('src', this.dataURL);
filename = this.fileInput.val().replace(FILENAMEREGEX, '');
return this.filename.text(filename);
- };
+ }
- GitLabCrop.prototype.setBlob = function() {
+ setBlob() {
this.dataURL = this.modalCropImg.cropper('getCroppedCanvas', {
width: 200,
height: 200
}).toDataURL('image/png');
return this.croppedImageBlob = this.dataURLtoBlob(this.dataURL);
- };
+ }
- GitLabCrop.prototype.getBlob = function() {
+ getBlob() {
return this.croppedImageBlob;
- };
-
- return GitLabCrop;
-
- })();
+ }
+ }
$.fn.glCrop = function(opts) {
return this.each(function() {
return $(this).data('glcrop', new GitLabCrop(this, opts));
});
- };
+ }
-}).call(this);
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
deleted file mode 100644
index 60f9fba5777..00000000000
--- a/app/assets/javascripts/profile/profile.js
+++ /dev/null
@@ -1,106 +0,0 @@
-(function() {
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-
- this.Profile = (function() {
- function Profile(opts) {
- var cropOpts, ref;
- if (opts == null) {
- opts = {};
- }
- this.onSubmitForm = bind(this.onSubmitForm, this);
- this.form = (ref = opts.form) != null ? ref : $('.edit-user');
- $('.js-preferences-form').on('change.preference', 'input[type=radio]', function() {
- return $(this).parents('form').submit();
- // Automatically submit the Preferences form when any of its radio buttons change
- });
- $('#user_notification_email').on('change', function() {
- return $(this).parents('form').submit();
- // Automatically submit email form when it changes
- });
- $('.update-username').on('ajax:before', function() {
- $('.loading-username').show();
- $(this).find('.update-success').hide();
- return $(this).find('.update-failed').hide();
- });
- $('.update-username').on('ajax:complete', function() {
- $('.loading-username').hide();
- $(this).find('.btn-save').enable();
- return $(this).find('.loading-gif').hide();
- });
- $('.update-notifications').on('ajax:success', function(e, data) {
- if (data.saved) {
- return new Flash("Notification settings saved", "notice");
- } else {
- return new Flash("Failed to save new settings", "alert");
- }
- });
- this.bindEvents();
- cropOpts = {
- filename: '.js-avatar-filename',
- previewImage: '.avatar-image .avatar',
- modalCrop: '.modal-profile-crop',
- pickImageEl: '.js-choose-user-avatar-button',
- uploadImageBtn: '.js-upload-user-avatar',
- modalCropImg: '.modal-profile-crop-image'
- };
- this.avatarGlCrop = $('.js-user-avatar-input').glCrop(cropOpts).data('glcrop');
- }
-
- Profile.prototype.bindEvents = function() {
- return this.form.on('submit', this.onSubmitForm);
- };
-
- Profile.prototype.onSubmitForm = function(e) {
- e.preventDefault();
- return this.saveForm();
- };
-
- Profile.prototype.saveForm = function() {
- var avatarBlob, formData, self;
- self = this;
- formData = new FormData(this.form[0]);
- avatarBlob = this.avatarGlCrop.getBlob();
- if (avatarBlob != null) {
- formData.append('user[avatar]', avatarBlob, 'avatar.png');
- }
- return $.ajax({
- url: this.form.attr('action'),
- type: this.form.attr('method'),
- data: formData,
- dataType: "json",
- processData: false,
- contentType: false,
- success: function(response) {
- return new Flash(response.message, 'notice');
- },
- error: function(jqXHR) {
- return new Flash(jqXHR.responseJSON.message, 'alert');
- },
- complete: function() {
- window.scrollTo(0, 0);
- // Enable submit button after requests ends
- return self.form.find(':input[disabled]').enable();
- }
- });
- };
-
- return Profile;
-
- })();
-
- $(function() {
- $(document).on('focusout.ssh_key', '#key_key', function() {
- var $title, comment;
- $title = $('#key_title');
- comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
- if (comment && comment.length > 1 && $title.val() === '') {
- return $title.val(comment[1]).change();
- }
- // Extract the SSH Key title from its comment
- });
- if (gl.utils.getPagePath() === 'profiles') {
- return new Profile();
- }
- });
-
-}).call(this);
diff --git a/app/assets/javascripts/profile/profile.js.es6 b/app/assets/javascripts/profile/profile.js.es6
new file mode 100644
index 00000000000..3eb81808bd6
--- /dev/null
+++ b/app/assets/javascripts/profile/profile.js.es6
@@ -0,0 +1,100 @@
+/* eslint-disable */
+((global) => {
+
+ class Profile {
+ constructor({ form } = {}) {
+ this.onSubmitForm = this.onSubmitForm.bind(this);
+ this.form = form || $('.edit-user');
+ this.bindEvents();
+ this.initAvatarGlCrop();
+ }
+
+ initAvatarGlCrop() {
+ const cropOpts = {
+ filename: '.js-avatar-filename',
+ previewImage: '.avatar-image .avatar',
+ modalCrop: '.modal-profile-crop',
+ pickImageEl: '.js-choose-user-avatar-button',
+ uploadImageBtn: '.js-upload-user-avatar',
+ modalCropImg: '.modal-profile-crop-image'
+ };
+ this.avatarGlCrop = $('.js-user-avatar-input').glCrop(cropOpts).data('glcrop');
+ }
+
+ bindEvents() {
+ $('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
+ $('#user_notification_email').on('change', this.submitForm);
+ $('.update-username').on('ajax:before', this.beforeUpdateUsername);
+ $('.update-username').on('ajax:complete', this.afterUpdateUsername);
+ $('.update-notifications').on('ajax:success', this.onUpdateNotifs);
+ this.form.on('submit', this.onSubmitForm);
+ }
+
+ submitForm() {
+ return $(this).parents('form').submit();
+ }
+
+ onSubmitForm(e) {
+ return this.saveForm();
+ }
+
+ beforeUpdateUsername() {
+ $('.loading-username').show();
+ $(this).find('.update-success').hide();
+ return $(this).find('.update-failed').hide();
+ }
+
+ afterUpdateUsername() {
+ $('.loading-username').hide();
+ $(this).find('.btn-save').enable();
+ return $(this).find('.loading-gif').hide();
+ }
+
+ onUpdateNotifs(e, data) {
+ return data.saved ?
+ new Flash("Notification settings saved", "notice") :
+ new Flash("Failed to save new settings", "alert");
+ }
+
+ saveForm() {
+ const self = this;
+ const formData = new FormData(this.form[0]);
+ const avatarBlob = this.avatarGlCrop.getBlob();
+
+ if (avatarBlob != null) {
+ formData.append('user[avatar]', avatarBlob, 'avatar.png');
+ }
+
+ return $.ajax({
+ url: this.form.attr('action'),
+ type: this.form.attr('method'),
+ data: formData,
+ dataType: "json",
+ processData: false,
+ contentType: false,
+ success: response => new Flash(response.message, 'notice'),
+ error: jqXHR => new Flash(jqXHR.responseJSON.message, 'alert'),
+ complete: () => {
+ window.scrollTo(0, 0);
+ // Enable submit button after requests ends
+ return self.form.find(':input[disabled]').enable();
+ }
+ });
+ }
+ }
+
+ $(function() {
+ $(document).on('focusout.ssh_key', '#key_key', function() {
+ const $title = $('#key_title');
+ const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
+ if (comment && comment.length > 1 && $title.val() === '') {
+ return $title.val(comment[1]).change();
+ }
+ // Extract the SSH Key title from its comment
+ });
+ if (global.utils.getPagePath() === 'profiles') {
+ return new Profile();
+ }
+ });
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/profile/profile_bundle.js b/app/assets/javascripts/profile/profile_bundle.js
index d6e4d9f7ad8..f50802bdf2e 100644
--- a/app/assets/javascripts/profile/profile_bundle.js
+++ b/app/assets/javascripts/profile/profile_bundle.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren */
/*= require_tree . */
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index a6c015299a0..016d999d77e 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, consistent-return, no-undef, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, no-else-return, newline-per-chained-call, no-shadow, semi, vars-on-top, indent, prefer-template, padded-blocks, max-len */
(function() {
this.Project = (function() {
function Project() {
@@ -23,16 +24,12 @@
return $(this).parents('form').submit();
});
$('.hide-no-ssh-message').on('click', function(e) {
- $.cookie('hide_no_ssh_message', 'false', {
- path: gon.relative_url_root || '/'
- });
+ Cookies.set('hide_no_ssh_message', 'false');
$(this).parents('.no-ssh-key-message').remove();
return e.preventDefault();
});
$('.hide-no-password-message').on('click', function(e) {
- $.cookie('hide_no_password_message', 'false', {
- path: gon.relative_url_root || '/'
- });
+ Cookies.set('hide_no_password_message', 'false');
$(this).parents('.no-password-message').remove();
return e.preventDefault();
});
@@ -82,7 +79,7 @@
if (ref.header != null) {
return $('<li />').addClass('dropdown-header').text(ref.header);
} else {
- link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref));
+ link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', ref);
return $('<li />').append(link);
}
},
diff --git a/app/assets/javascripts/project_avatar.js b/app/assets/javascripts/project_avatar.js
index 277e71523d5..84f28ede4bf 100644
--- a/app/assets/javascripts/project_avatar.js
+++ b/app/assets/javascripts/project_avatar.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-useless-escape, padded-blocks, max-len */
(function() {
this.ProjectAvatar = (function() {
function ProjectAvatar() {
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 5bf900f3e1d..804306a3293 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, no-undef, object-shorthand, no-param-reassign, comma-dangle, no-plusplus, prefer-template, no-unused-vars, no-return-assign, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -36,16 +37,6 @@
}
};
})(this));
- return this.element.find(".tree-content-holder .tree-table").on("click", function(event) {
- var path;
- if (event.target.nodeName !== "A") {
- path = this.element.find(".tree-item-file-name a", this).attr("href");
- if (path) {
- return location.href = path;
- }
- }
- });
- // init event
};
ProjectFindFile.prototype.findFile = function() {
@@ -121,11 +112,12 @@
// make tbody row html
ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) {
var $tr;
- $tr = $("<tr class='tree-item'><td class='tree-item-file-name'><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'><a></a></span></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));
} else {
- $tr.find("a").attr("href", blobItemUrl).text(filePath);
+ $tr.find("a").attr("href", blobItemUrl);
+ $tr.find(".str-truncated").text(filePath);
}
return $tr;
};
@@ -165,10 +157,10 @@
};
ProjectFindFile.prototype.goToBlob = function() {
- var path;
- path = this.element.find(".tree-item.selected .tree-item-file-name a").attr("href");
- if (path) {
- return location.href = path;
+ 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_fork.js b/app/assets/javascripts/project_fork.js
index d2261c51f35..4aedc9a2330 100644
--- a/app/assets/javascripts/project_fork.js
+++ b/app/assets/javascripts/project_fork.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, padded-blocks, max-len */
(function() {
this.ProjectFork = (function() {
function ProjectFork() {
diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js
index c61b0cf2fde..c99e55234cf 100644
--- a/app/assets/javascripts/project_import.js
+++ b/app/assets/javascripts/project_import.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-undef, padded-blocks, max-len */
(function() {
this.ProjectImport = (function() {
function ProjectImport() {
diff --git a/app/assets/javascripts/project_label_subscription.js.es6 b/app/assets/javascripts/project_label_subscription.js.es6
new file mode 100644
index 00000000000..03a115cb35b
--- /dev/null
+++ b/app/assets/javascripts/project_label_subscription.js.es6
@@ -0,0 +1,53 @@
+/* eslint-disable */
+(function(global) {
+ class ProjectLabelSubscription {
+ constructor(container) {
+ this.$container = $(container);
+ this.$buttons = this.$container.find('.js-subscribe-button');
+
+ this.$buttons.on('click', this.toggleSubscription.bind(this));
+ }
+
+ toggleSubscription(event) {
+ event.preventDefault();
+
+ const $btn = $(event.currentTarget);
+ const $span = $btn.find('span');
+ const url = $btn.attr('data-url');
+ const oldStatus = $btn.attr('data-status');
+
+ $btn.addClass('disabled');
+ $span.toggleClass('hidden');
+
+ $.ajax({
+ type: 'POST',
+ url: url
+ }).done(() => {
+ let newStatus, newAction;
+
+ if (oldStatus === 'unsubscribed') {
+ [newStatus, newAction] = ['subscribed', 'Unsubscribe'];
+ } else {
+ [newStatus, newAction] = ['unsubscribed', 'Subscribe'];
+ }
+
+ $span.toggleClass('hidden');
+ $btn.removeClass('disabled');
+
+ this.$buttons.attr('data-status', newStatus);
+ this.$buttons.find('> span').text(newAction);
+
+ for (let button of this.$buttons) {
+ let $button = $(button);
+
+ if ($button.attr('data-original-title')) {
+ $button.tooltip('hide').attr('data-original-title', newAction).tooltip('fixTitle');
+ }
+ }
+ });
+ }
+ }
+
+ global.ProjectLabelSubscription = ProjectLabelSubscription;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/project_members.js b/app/assets/javascripts/project_members.js
deleted file mode 100644
index 78f7b48bc7d..00000000000
--- a/app/assets/javascripts/project_members.js
+++ /dev/null
@@ -1,10 +0,0 @@
-(function() {
- this.ProjectMembers = (function() {
- function ProjectMembers() {
- $('li.project_member').bind('ajax:success', function() {
- return $(this).fadeOut();
- });
- }
- return ProjectMembers;
- })();
-}).call(this);
diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js
index a787b11f2a9..7fc611d0dad 100644
--- a/app/assets/javascripts/project_new.js
+++ b/app/assets/javascripts/project_new.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-unused-vars, one-var, indent, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, radix, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -5,6 +6,7 @@
function ProjectNew() {
this.toggleSettings = bind(this.toggleSettings, this);
this.$selects = $('.features select');
+ this.$repoSelects = this.$selects.filter('.js-repo-select');
$('.project-edit-container').on('ajax:before', (function(_this) {
return function() {
@@ -14,6 +16,7 @@
})(this));
this.toggleSettings();
this.toggleSettingsOnclick();
+ this.toggleRepoVisibility();
}
ProjectNew.prototype.toggleSettings = function() {
@@ -41,6 +44,49 @@
}
};
+ ProjectNew.prototype.toggleRepoVisibility = function () {
+ var $repoAccessLevel = $('.js-repo-access-level select'),
+ containerRegistry = document.querySelectorAll('.js-container-registry')[0],
+ containerRegistryCheckbox = document.getElementById('project_container_registry_enabled');
+
+ this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']")
+ .nextAll()
+ .hide();
+
+ $repoAccessLevel.off('change')
+ .on('change', function () {
+ var selectedVal = parseInt($repoAccessLevel.val());
+
+ this.$repoSelects.each(function () {
+ var $this = $(this),
+ repoSelectVal = parseInt($this.val());
+
+ $this.find('option').show();
+
+ if (selectedVal < repoSelectVal) {
+ $this.val(selectedVal);
+ }
+
+ $this.find("option[value='" + selectedVal + "']").nextAll().hide();
+ });
+
+ if (selectedVal) {
+ this.$repoSelects.removeClass('disabled');
+
+ if (containerRegistry) {
+ containerRegistry.style.display = '';
+ }
+ } else {
+ this.$repoSelects.addClass('disabled');
+
+ if (containerRegistry) {
+ containerRegistry.style.display = 'none';
+ containerRegistryCheckbox.checked = false;
+ }
+ }
+ }.bind(this));
+ };
+
return ProjectNew;
})();
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index 20b147500cf..fe1f96872f3 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-undef, no-else-return, quotes, padded-blocks, max-len */
(function() {
this.ProjectSelect = (function() {
function ProjectSelect() {
@@ -23,7 +24,7 @@
data = groups.concat(projects);
return finalCallback(data);
};
- return Api.groups(term, false, groupsCallback);
+ return Api.groups(term, {}, groupsCallback);
};
} else {
projectsCallback = finalCallback;
@@ -72,7 +73,7 @@
data = groups.concat(projects);
return finalCallback(data);
};
- return Api.groups(query.term, false, groupsCallback);
+ return Api.groups(query.term, {}, groupsCallback);
};
} else {
projectsCallback = finalCallback;
diff --git a/app/assets/javascripts/project_show.js b/app/assets/javascripts/project_show.js
index c8cfc9a9ba8..eaf4c03d573 100644
--- a/app/assets/javascripts/project_show.js
+++ b/app/assets/javascripts/project_show.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, padded-blocks */
(function() {
this.ProjectShow = (function() {
function ProjectShow() {}
diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js
index 04fb49552e8..dbf530bed41 100644
--- a/app/assets/javascripts/projects_list.js
+++ b/app/assets/javascripts/projects_list.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-var, one-var, one-var-declaration-per-line, no-undef, prefer-arrow-callback, consistent-return, no-unused-vars, camelcase, prefer-template, comma-dangle, padded-blocks, max-len */
(function() {
this.ProjectsList = {
init: function() {
diff --git a/app/assets/javascripts/protected_branch_access_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6
index 7aeb5f92514..2d60947a666 100644
--- a/app/assets/javascripts/protected_branch_access_dropdown.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branch_create.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_create.js.es6
index 46beca469b9..c45c9d8ff22 100644
--- a/app/assets/javascripts/protected_branch_create.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_create.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
index 983322cbecc..e3f226e9a2a 100644
--- a/app/assets/javascripts/protected_branch_dropdown.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
class ProtectedBranchDropdown {
constructor(options) {
this.onSelect = options.onSelect;
diff --git a/app/assets/javascripts/protected_branch_edit.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6
index 40bc4adb71b..ac3142ffb07 100644
--- a/app/assets/javascripts/protected_branch_edit.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
@@ -40,7 +41,6 @@
dataType: 'json',
data: {
_method: 'PATCH',
- id: this.$wrap.data('banchId'),
protected_branch: {
merge_access_levels_attributes: [{
id: this.$allowedToMergeDropdown.data('access-level-id'),
diff --git a/app/assets/javascripts/protected_branch_edit_list.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6
index 9ff0fd12c76..705378a364d 100644
--- a/app/assets/javascripts/protected_branch_edit_list.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js
new file mode 100644
index 00000000000..17e34163831
--- /dev/null
+++ b/app/assets/javascripts/protected_branches/protected_branches_bundle.js
@@ -0,0 +1,2 @@
+/* eslint-disable */
+/*= require_tree . */
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index e3d5f413c77..440b5da756d 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-unused-vars, semi, consistent-return, one-var, one-var-declaration-per-line, no-undef, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -5,15 +6,24 @@
function Sidebar(currentUser) {
this.toggleTodo = bind(this.toggleTodo, this);
this.sidebar = $('aside');
+ this.removeListeners();
this.addEventListeners();
}
+ Sidebar.prototype.removeListeners = function () {
+ this.sidebar.off('click', '.sidebar-collapsed-icon');
+ $('.dropdown').off('hidden.gl.dropdown');
+ $('.dropdown').off('loading.gl.dropdown');
+ $('.dropdown').off('loaded.gl.dropdown');
+ $(document).off('click', '.js-sidebar-toggle');
+ }
+
Sidebar.prototype.addEventListeners = function() {
this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked);
$('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
$('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
- $(document).off('click', '.js-sidebar-toggle').on('click', '.js-sidebar-toggle', function(e, triggered) {
+ $(document).on('click', '.js-sidebar-toggle', function(e, triggered) {
var $allGutterToggleIcons, $this, $thisIcon;
e.preventDefault();
$this = $(this);
@@ -29,9 +39,7 @@
$('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
}
if (!triggered) {
- return $.cookie("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'), {
- path: gon.relative_url_root || '/'
- });
+ return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'));
}
});
return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo);
@@ -74,16 +82,11 @@
};
Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
- var $todoPendingCount;
- $todoPendingCount = $('.todos-pending-count');
- $todoPendingCount.text(data.count);
+ $(document).trigger('todo:toggle', data.count);
+
$btn.enable();
$todoLoading.addClass('hidden');
- if (data.count === 0) {
- $todoPendingCount.addClass('hidden');
- } else {
- $todoPendingCount.removeClass('hidden');
- }
+
if (data.delete_path != null) {
$btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
return $btnText.text($btn.data('mark-text'));
diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js
index d34346f862b..1d208f1494c 100644
--- a/app/assets/javascripts/search.js
+++ b/app/assets/javascripts/search.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, no-undef, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, padded-blocks, max-len */
(function() {
this.Search = (function() {
function Search() {
@@ -10,7 +11,7 @@
filterable: true,
fieldName: 'group_id',
data: function(term, callback) {
- return Api.groups(term, null, function(data) {
+ return Api.groups(term, {}, function(data) {
data.unshift({
name: 'Any'
});
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js.es6
index 8abb09c626f..5fa94556501 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js.es6
@@ -1,30 +1,22 @@
-(function() {
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-
- this.SearchAutocomplete = (function() {
- var KEYCODE;
-
- KEYCODE = {
- ESCAPE: 27,
- BACKSPACE: 8,
- ENTER: 13,
- UP: 38,
- DOWN: 40
- };
+/* eslint-disable */
+((global) => {
- function SearchAutocomplete(opts) {
- var ref, ref1, ref2, ref3, ref4;
- if (opts == null) {
- opts = {};
- }
- this.onSearchInputBlur = bind(this.onSearchInputBlur, this);
- this.onClearInputClick = bind(this.onClearInputClick, this);
- this.onSearchInputFocus = bind(this.onSearchInputFocus, this);
- this.onSearchInputClick = bind(this.onSearchInputClick, this);
- this.onSearchInputKeyUp = bind(this.onSearchInputKeyUp, this);
- this.onSearchInputKeyDown = bind(this.onSearchInputKeyDown, this);
- this.wrap = (ref = opts.wrap) != null ? ref : $('.search'), this.optsEl = (ref1 = opts.optsEl) != null ? ref1 : this.wrap.find('.search-autocomplete-opts'), this.autocompletePath = (ref2 = opts.autocompletePath) != null ? ref2 : this.optsEl.data('autocomplete-path'), this.projectId = (ref3 = opts.projectId) != null ? ref3 : this.optsEl.data('autocomplete-project-id') || '', this.projectRef = (ref4 = opts.projectRef) != null ? ref4 : this.optsEl.data('autocomplete-project-ref') || '';
- // Dropdown Element
+ const KEYCODE = {
+ ESCAPE: 27,
+ BACKSPACE: 8,
+ ENTER: 13,
+ UP: 38,
+ DOWN: 40
+ };
+
+ class SearchAutocomplete {
+ constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) {
+ this.bindEventContext();
+ this.wrap = wrap || $('.search');
+ this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts');
+ this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path');
+ this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || '');
+ this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || '');
this.dropdown = this.wrap.find('.dropdown');
this.dropdownContent = this.dropdown.find('.dropdown-content');
this.locationBadgeEl = this.getElement('.location-badge');
@@ -46,19 +38,27 @@
}
// Finds an element inside wrapper element
- SearchAutocomplete.prototype.getElement = function(selector) {
+ bindEventContext() {
+ this.onSearchInputBlur = this.onSearchInputBlur.bind(this);
+ this.onClearInputClick = this.onClearInputClick.bind(this);
+ this.onSearchInputFocus = this.onSearchInputFocus.bind(this);
+ this.onSearchInputClick = this.onSearchInputClick.bind(this);
+ this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this);
+ this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this);
+ }
+ getElement(selector) {
return this.wrap.find(selector);
- };
+ }
- SearchAutocomplete.prototype.saveOriginalState = function() {
+ saveOriginalState() {
return this.originalState = this.serializeState();
- };
+ }
- SearchAutocomplete.prototype.saveTextLength = function() {
+ saveTextLength() {
return this.lastTextLength = this.searchInput.val().length;
- };
+ }
- SearchAutocomplete.prototype.createAutocomplete = function() {
+ createAutocomplete() {
return this.searchInput.glDropdown({
filterInputBlur: false,
filterable: true,
@@ -73,9 +73,9 @@
selectable: true,
clicked: this.onClick.bind(this)
});
- };
+ }
- SearchAutocomplete.prototype.getData = function(term, callback) {
+ getData(term, callback) {
var _this, contents, jqXHR;
_this = this;
if (!term) {
@@ -138,9 +138,9 @@
}).always(function() {
return _this.loadingSuggestions = false;
});
- };
+ }
- SearchAutocomplete.prototype.getCategoryContents = function() {
+ getCategoryContents() {
var dashboardOptions, groupOptions, issuesPath, items, mrPath, name, options, projectOptions, userId, utils;
userId = gon.current_user_id;
utils = gl.utils, projectOptions = gl.projectOptions, groupOptions = gl.groupOptions, dashboardOptions = gl.dashboardOptions;
@@ -173,9 +173,9 @@
items.splice(0, 1);
}
return items;
- };
+ }
- SearchAutocomplete.prototype.serializeState = function() {
+ serializeState() {
return {
// Search Criteria
search_project_id: this.projectInputEl.val(),
@@ -186,9 +186,9 @@
// Location badge
_location: this.locationBadgeEl.text()
};
- };
+ }
- SearchAutocomplete.prototype.bindEvents = function() {
+ bindEvents() {
this.searchInput.on('keydown', this.onSearchInputKeyDown);
this.searchInput.on('keyup', this.onSearchInputKeyUp);
this.searchInput.on('click', this.onSearchInputClick);
@@ -200,9 +200,9 @@
return _this.searchInput.focus();
};
})(this));
- };
+ }
- SearchAutocomplete.prototype.enableAutocomplete = function() {
+ enableAutocomplete() {
var _this;
// No need to enable anything if user is not logged in
if (!gon.current_user_id) {
@@ -216,12 +216,12 @@
}
};
- SearchAutocomplete.prototype.onSearchInputKeyDown = function() {
// Saves last length of the entered text
+ onSearchInputKeyDown() {
return this.saveTextLength();
- };
+ }
- SearchAutocomplete.prototype.onSearchInputKeyUp = function(e) {
+ onSearchInputKeyUp(e) {
switch (e.keyCode) {
case KEYCODE.BACKSPACE:
// when trying to remove the location badge
@@ -259,54 +259,53 @@
}
}
this.wrap.toggleClass('has-value', !!e.target.value);
- };
+ }
// Avoid falsy value to be returned
- SearchAutocomplete.prototype.onSearchInputClick = function(e) {
- // Prevents closing the dropdown menu
+ onSearchInputClick(e) {
return e.stopImmediatePropagation();
- };
+ }
- SearchAutocomplete.prototype.onSearchInputFocus = function() {
+ onSearchInputFocus() {
this.isFocused = true;
this.wrap.addClass('search-active');
if (this.getValue() === '') {
return this.getData();
}
- };
+ }
- SearchAutocomplete.prototype.getValue = function() {
+ getValue() {
return this.searchInput.val();
- };
+ }
- SearchAutocomplete.prototype.onClearInputClick = function(e) {
+ onClearInputClick(e) {
e.preventDefault();
return this.searchInput.val('').focus();
- };
+ }
- SearchAutocomplete.prototype.onSearchInputBlur = function(e) {
+ onSearchInputBlur(e) {
this.isFocused = false;
this.wrap.removeClass('search-active');
// If input is blank then restore state
if (this.searchInput.val() === '') {
return this.restoreOriginalState();
}
- };
+ }
- SearchAutocomplete.prototype.addLocationBadge = function(item) {
+ addLocationBadge(item) {
var badgeText, category, value;
category = item.category != null ? item.category + ": " : '';
value = item.value != null ? item.value : '';
badgeText = "" + category + value;
this.locationBadgeEl.text(badgeText).show();
return this.wrap.addClass('has-location-badge');
- };
+ }
- SearchAutocomplete.prototype.hasLocationBadge = function() {
+ hasLocationBadge() {
return this.wrap.is('.has-location-badge');
};
- SearchAutocomplete.prototype.restoreOriginalState = function() {
+ restoreOriginalState() {
var i, input, inputs, len;
inputs = Object.keys(this.originalState);
for (i = 0, len = inputs.length; i < len; i++) {
@@ -320,13 +319,13 @@
value: this.originalState._location
});
}
- };
+ }
- SearchAutocomplete.prototype.badgePresent = function() {
+ badgePresent() {
return this.locationBadgeEl.length;
- };
+ }
- SearchAutocomplete.prototype.resetSearchState = function() {
+ resetSearchState() {
var i, input, inputs, len, results;
inputs = Object.keys(this.originalState);
results = [];
@@ -339,30 +338,30 @@
results.push(this.getElement("#" + input).val(''));
}
return results;
- };
+ }
- SearchAutocomplete.prototype.removeLocationBadge = function() {
+ removeLocationBadge() {
this.locationBadgeEl.hide();
this.resetSearchState();
this.wrap.removeClass('has-location-badge');
return this.disableAutocomplete();
- };
+ }
- SearchAutocomplete.prototype.disableAutocomplete = function() {
+ disableAutocomplete() {
if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) {
this.searchInput.addClass('disabled');
this.dropdown.removeClass('open').trigger('hidden.bs.dropdown');
this.restoreMenu();
}
- };
+ }
- SearchAutocomplete.prototype.restoreMenu = function() {
+ restoreMenu() {
var html;
html = "<ul> <li><a class='dropdown-menu-empty-link is-focused'>Loading...</a></li> </ul>";
return this.dropdownContent.html(html);
};
- SearchAutocomplete.prototype.onClick = function(item, $el, e) {
+ onClick(item, $el, e) {
if (location.pathname.indexOf(item.url) !== -1) {
e.preventDefault();
if (!this.badgePresent) {
@@ -385,8 +384,45 @@
}
};
- return SearchAutocomplete;
+ }
+
+ global.SearchAutocomplete = SearchAutocomplete;
+
+ $(function() {
+ var $projectOptionsDataEl = $('.js-search-project-options');
+ var $groupOptionsDataEl = $('.js-search-group-options');
+ var $dashboardOptionsDataEl = $('.js-search-dashboard-options');
+
+ if ($projectOptionsDataEl.length) {
+ gl.projectOptions = gl.projectOptions || {};
+
+ var projectPath = $projectOptionsDataEl.data('project-path');
+
+ gl.projectOptions[projectPath] = {
+ name: $projectOptionsDataEl.data('name'),
+ issuesPath: $projectOptionsDataEl.data('issues-path'),
+ mrPath: $projectOptionsDataEl.data('mr-path')
+ };
+ }
- })();
+ if ($groupOptionsDataEl.length) {
+ gl.groupOptions = gl.groupOptions || {};
+
+ var groupPath = $groupOptionsDataEl.data('group-path');
+
+ gl.groupOptions[groupPath] = {
+ name: $groupOptionsDataEl.data('name'),
+ issuesPath: $groupOptionsDataEl.data('issues-path'),
+ mrPath: $groupOptionsDataEl.data('mr-path')
+ };
+ }
+
+ if ($dashboardOptionsDataEl.length) {
+ gl.dashboardOptions = {
+ issuesPath: $dashboardOptionsDataEl.data('issues-path'),
+ mrPath: $dashboardOptionsDataEl.data('mr-path')
+ };
+ }
+ });
-}).call(this);
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js
index 3aa8536d40a..fa2168723be 100644
--- a/app/assets/javascripts/shortcuts.js
+++ b/app/assets/javascripts/shortcuts.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-undef, prefer-arrow-callback, consistent-return, object-shorthand, no-unused-vars, one-var, one-var-declaration-per-line, no-plusplus, no-else-return, comma-dangle, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/shortcuts_blob.js b/app/assets/javascripts/shortcuts_blob.js
index b931eab638f..65305b8c22f 100644
--- a/app/assets/javascripts/shortcuts_blob.js
+++ b/app/assets/javascripts/shortcuts_blob.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, consistent-return, padded-blocks, no-undef, max-len */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/shortcuts_dashboard_navigation.js
index f7492a2aa5c..1b9a265ba39 100644
--- a/app/assets/javascripts/shortcuts_dashboard_navigation.js
+++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-arrow-callback, consistent-return, no-return-assign, padded-blocks, no-undef, max-len */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js
index 92ce31969e3..68cd6fad04e 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/shortcuts_find_file.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, padded-blocks, no-undef, max-len */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index 235bf4f95ec..c4899f3566a 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, one-var-declaration-per-line, quotes, prefer-arrow-callback, consistent-return, prefer-template, no-mixed-operators, no-undef, padded-blocks, max-len */
/*= require mousetrap */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
index b04159420d1..7d4d6364c70 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/shortcuts_navigation.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-arrow-callback, consistent-return, no-return-assign, padded-blocks, no-undef, max-len */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/shortcuts_network.js
index fb2b39e757e..a4095d2c06b 100644
--- a/app/assets/javascripts/shortcuts_network.js
+++ b/app/assets/javascripts/shortcuts_network.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, max-len, no-var, one-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, padded-blocks, no-undef, max-len */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6
index bb5184ae881..126f141912d 100644
--- a/app/assets/javascripts/sidebar.js.es6
+++ b/app/assets/javascripts/sidebar.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
((global) => {
let singleton;
@@ -28,7 +29,7 @@
}
init() {
- this.isPinned = $.cookie(pinnedStateCookie) === 'true';
+ this.isPinned = Cookies.get(pinnedStateCookie) === 'true';
this.isExpanded = (
window.innerWidth >= sidebarBreakpoint &&
$(pageSelector).hasClass(expandedPageClass)
@@ -37,7 +38,9 @@
.on('click', sidebarToggleSelector, () => this.toggleSidebar())
.on('click', pinnedToggleSelector, () => this.togglePinnedState())
.on('click', 'html, body', (e) => this.handleClickEvent(e))
- .on('page:change', () => this.renderState());
+ .on('page:change', () => this.renderState())
+ .on('todo:toggle', (e, count) => this.updateTodoCount(count));
+
$(window)
.off('resize.sidebar')
.on('resize.sidebar', () => {
@@ -52,6 +55,7 @@
}
}, 250);
});
+
this.renderState();
}
@@ -66,6 +70,10 @@
}
}
+ updateTodoCount(count) {
+ $('.js-todos-count').text(gl.text.addDelimiter(count));
+ }
+
toggleSidebar() {
this.isExpanded = !this.isExpanded;
this.renderState();
@@ -76,10 +84,7 @@
if (!this.isPinned) {
this.isExpanded = false;
}
- $.cookie(pinnedStateCookie, this.isPinned ? 'true' : 'false', {
- path: gon.relative_url_root || '/',
- expires: 3650
- });
+ Cookies.set(pinnedStateCookie, this.isPinned ? 'true' : 'false', { expires: 3650 });
this.renderState();
}
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 156b9b8abec..2767849e673 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, padded-blocks, no-undef, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -10,12 +11,13 @@
ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
- COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. Click to expand it.</div>';
+ COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
- function SingleFileDiff(file) {
+ function SingleFileDiff(file, forceLoad, cb) {
this.file = file;
this.toggleDiff = bind(this.toggleDiff, this);
this.content = $('.diff-content', this.file);
+ this.$toggleIcon = $('.diff-toggle-caret', this.file);
this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path');
this.isOpen = !this.diffForPath;
if (this.diffForPath) {
@@ -23,34 +25,43 @@
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.content.after(this.collapsedContent);
+ this.$toggleIcon.addClass('fa-caret-down');
+ }
+ $('.file-title, .click-to-expand', this.file).on('click', this.toggleDiff);
+ if (forceLoad) {
+ this.toggleDiff(null, cb);
}
- this.collapsedContent.on('click', this.toggleDiff);
- $('.file-title > a', this.file).on('click', this.toggleDiff);
}
- SingleFileDiff.prototype.toggleDiff = function(e) {
+ SingleFileDiff.prototype.toggleDiff = function(e, cb) {
+ var $target = $(e.target);
+ if (!$target.hasClass('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();
+ this.$toggleIcon.addClass('fa-caret-right').removeClass('fa-caret-down');
this.collapsedContent.show();
- if (typeof DiffNotesApp !== 'undefined') {
- DiffNotesApp.compileComponents();
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
}
} else if (this.content) {
this.collapsedContent.hide();
this.content.show();
- if (typeof DiffNotesApp !== 'undefined') {
- DiffNotesApp.compileComponents();
+ this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
}
} else {
- return this.getContentHTML();
+ this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
+ return this.getContentHTML(cb);
}
};
- SingleFileDiff.prototype.getContentHTML = function() {
+ SingleFileDiff.prototype.getContentHTML = function(cb) {
this.collapsedContent.hide();
this.loadingContent.show();
$.get(this.diffForPath, (function(_this) {
@@ -65,9 +76,11 @@
}
_this.collapsedContent.after(_this.content);
- if (typeof DiffNotesApp !== 'undefined') {
- DiffNotesApp.compileComponents();
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
}
+
+ if (cb) cb();
};
})(this));
};
@@ -76,10 +89,10 @@
})();
- $.fn.singleFileDiff = function() {
+ $.fn.singleFileDiff = function(forceLoad, cb) {
return this.each(function() {
- if (!$.data(this, 'singleFileDiff')) {
- return $.data(this, 'singleFileDiff', new SingleFileDiff(this));
+ if (!$.data(this, 'singleFileDiff') || forceLoad) {
+ return $.data(this, 'singleFileDiff', new SingleFileDiff(this, forceLoad, cb));
}
});
};
diff --git a/app/assets/javascripts/smart_interval.js.es6 b/app/assets/javascripts/smart_interval.js.es6
new file mode 100644
index 00000000000..5eb15dba79b
--- /dev/null
+++ b/app/assets/javascripts/smart_interval.js.es6
@@ -0,0 +1,130 @@
+/*
+* Instances of SmartInterval extend the functionality of `setInterval`, make it configurable
+* and controllable by a public API.
+*
+* */
+
+(() => {
+ class SmartInterval {
+ /**
+ * @param { function } callback Function to be called on each iteration (required)
+ * @param { milliseconds } startingInterval `currentInterval` is set to this initially
+ * @param { milliseconds } maxInterval `currentInterval` will be incremented to this
+ * @param { integer } incrementByFactorOf `currentInterval` is incremented by this factor
+ * @param { boolean } lazyStart Configure if timer is initialized on instantiation or lazily
+ */
+ constructor({ callback, startingInterval, maxInterval, incrementByFactorOf, lazyStart }) {
+ this.cfg = {
+ callback,
+ startingInterval,
+ maxInterval,
+ incrementByFactorOf,
+ lazyStart,
+ };
+
+ this.state = {
+ intervalId: null,
+ currentInterval: startingInterval,
+ pageVisibility: 'visible',
+ };
+
+ this.initInterval();
+ }
+ /* public */
+
+ start() {
+ const cfg = this.cfg;
+ const state = this.state;
+
+ state.intervalId = window.setInterval(() => {
+ cfg.callback();
+
+ if (this.getCurrentInterval() === cfg.maxInterval) {
+ return;
+ }
+
+ this.incrementInterval();
+ this.resume();
+ }, this.getCurrentInterval());
+ }
+
+ // cancel the existing timer, setting the currentInterval back to startingInterval
+ cancel() {
+ this.setCurrentInterval(this.cfg.startingInterval);
+ this.stopTimer();
+ }
+
+ // start a timer, using the existing interval
+ resume() {
+ this.stopTimer(); // stop exsiting timer, in case timer was not previously stopped
+ this.start();
+ }
+
+ destroy() {
+ this.cancel();
+ $(document).off('visibilitychange').off('page:before-unload');
+ }
+
+ /* private */
+
+ initInterval() {
+ const cfg = this.cfg;
+
+ if (!cfg.lazyStart) {
+ this.start();
+ }
+
+ this.initVisibilityChangeHandling();
+ this.initPageUnloadHandling();
+ }
+
+ initVisibilityChangeHandling() {
+ // cancel interval when tab no longer shown (prevents cached pages from polling)
+ $(document)
+ .off('visibilitychange').on('visibilitychange', (e) => {
+ this.state.pageVisibility = e.target.visibilityState;
+ this.handleVisibilityChange();
+ });
+ }
+
+ initPageUnloadHandling() {
+ // prevent interval continuing after page change, when kept in cache by Turbolinks
+ $(document).on('page:before-unload', () => this.cancel());
+ }
+
+ handleVisibilityChange() {
+ const state = this.state;
+
+ const intervalAction = state.pageVisibility === 'hidden' ? this.cancel : this.resume;
+
+ intervalAction.apply(this);
+ }
+
+ getCurrentInterval() {
+ return this.state.currentInterval;
+ }
+
+ setCurrentInterval(newInterval) {
+ this.state.currentInterval = newInterval;
+ }
+
+ incrementInterval() {
+ const cfg = this.cfg;
+ const currentInterval = this.getCurrentInterval();
+ let nextInterval = currentInterval * cfg.incrementByFactorOf;
+
+ if (nextInterval > cfg.maxInterval) {
+ nextInterval = cfg.maxInterval;
+ }
+
+ this.setCurrentInterval(nextInterval);
+ }
+
+ stopTimer() {
+ const state = this.state;
+
+ state.intervalId = window.clearInterval(state.intervalId);
+ }
+ }
+ gl.SmartInterval = SmartInterval;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/snippet/snippet_bundle.js b/app/assets/javascripts/snippet/snippet_bundle.js
index 855e97eb301..2c8ecba7de4 100644
--- a/app/assets/javascripts/snippet/snippet_bundle.js
+++ b/app/assets/javascripts/snippet/snippet_bundle.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, no-undef, quotes, semi, padded-blocks, max-len */
/*= require_tree . */
(function() {
diff --git a/app/assets/javascripts/snippets_list.js.es6 b/app/assets/javascripts/snippets_list.js.es6
index 6f0996c0d2a..c3afc3f2246 100644
--- a/app/assets/javascripts/snippets_list.js.es6
+++ b/app/assets/javascripts/snippets_list.js.es6
@@ -1,3 +1,4 @@
+/* eslint-disable */
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index 10509313c12..32803fa790b 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, no-undef, padded-blocks, max-len */
(function() {
this.Star = (function() {
function Star() {
@@ -10,11 +11,9 @@
$this.parent().find('.star-count').text(data.star_count);
if (isStarred) {
$starSpan.removeClass('starred').text('Star');
- gl.utils.updateTooltipTitle($this, 'Star project');
$starIcon.removeClass('fa-star').addClass('fa-star-o');
} else {
$starSpan.addClass('starred').text('Unstar');
- gl.utils.updateTooltipTitle($this, 'Unstar project');
$starIcon.removeClass('fa-star-o').addClass('fa-star');
}
};
diff --git a/app/assets/javascripts/subbable_resource.js.es6 b/app/assets/javascripts/subbable_resource.js.es6
new file mode 100644
index 00000000000..932120157a3
--- /dev/null
+++ b/app/assets/javascripts/subbable_resource.js.es6
@@ -0,0 +1,54 @@
+//= require vue
+//= require vue-resource
+
+(() => {
+/*
+* SubbableResource can be extended to provide a pubsub-style service for one-off REST
+* calls. Subscribe by passing a callback or render method you will use to handle responses.
+ *
+* */
+
+ class SubbableResource {
+ constructor(resourcePath) {
+ this.endpoint = resourcePath;
+
+ // TODO: Switch to axios.create
+ this.resource = $.ajax;
+ this.subscribers = [];
+ }
+
+ subscribe(callback) {
+ this.subscribers.push(callback);
+ }
+
+ publish(newResponse) {
+ const responseCopy = _.extend({}, newResponse);
+ this.subscribers.forEach((fn) => {
+ fn(responseCopy);
+ });
+ return newResponse;
+ }
+
+ get(payload) {
+ return this.resource(payload)
+ .then(data => this.publish(data));
+ }
+
+ post(payload) {
+ return this.resource(payload)
+ .then(data => this.publish(data));
+ }
+
+ put(payload) {
+ return this.resource(payload)
+ .then(data => this.publish(data));
+ }
+
+ delete(payload) {
+ return this.resource(payload)
+ .then(data => this.publish(data));
+ }
+ }
+
+ gl.SubbableResource = SubbableResource;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js
index 5e3c5983d75..6d75688deeb 100644
--- a/app/assets/javascripts/subscription.js
+++ b/app/assets/javascripts/subscription.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, one-var, one-var-declaration-per-line, camelcase, consistent-return, no-undef, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -5,10 +6,10 @@
function Subscription(container) {
this.toggleSubscription = bind(this.toggleSubscription, this);
var $container;
- $container = $(container);
- this.url = $container.attr('data-url');
- this.subscribe_button = $container.find('.js-subscribe-button');
- this.subscription_status = $container.find('.subscription-status');
+ this.$container = $(container);
+ this.url = this.$container.attr('data-url');
+ this.subscribe_button = this.$container.find('.js-subscribe-button');
+ this.subscription_status = this.$container.find('.subscription-status');
this.subscribe_button.unbind('click').click(this.toggleSubscription);
}
@@ -18,17 +19,27 @@
action = btn.find('span').text();
current_status = this.subscription_status.attr('data-status');
btn.addClass('disabled');
+
+ if ($('html').hasClass('issue-boards-page')) {
+ this.url = this.$container.attr('data-url');
+ }
+
return $.post(this.url, (function(_this) {
return function() {
var status;
btn.removeClass('disabled');
- status = current_status === 'subscribed' ? 'unsubscribed' : 'subscribed';
- _this.subscription_status.attr('data-status', status);
- action = status === 'subscribed' ? 'Unsubscribe' : 'Subscribe';
- btn.find('span').text(action);
- _this.subscription_status.find('>div').toggleClass('hidden');
- if (btn.attr('data-original-title')) {
- return btn.tooltip('hide').attr('data-original-title', action).tooltip('fixTitle');
+
+ if ($('html').hasClass('issue-boards-page')) {
+ Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'subscribed', !gl.issueBoards.BoardsStore.detail.issue.subscribed);
+ } else {
+ status = current_status === 'subscribed' ? 'unsubscribed' : 'subscribed';
+ _this.subscription_status.attr('data-status', status);
+ action = status === 'subscribed' ? 'Unsubscribe' : 'Subscribe';
+ btn.find('span').text(action);
+ _this.subscription_status.find('>div').toggleClass('hidden');
+ if (btn.attr('data-original-title')) {
+ return btn.tooltip('hide').attr('data-original-title', action).tooltip('fixTitle');
+ }
}
};
})(this));
diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js
index d6c219603d1..185d20775d0 100644
--- a/app/assets/javascripts/subscription_select.js
+++ b/app/assets/javascripts/subscription_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */
(function() {
this.SubscriptionSelect = (function() {
function SubscriptionSelect() {
diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js
index 2ae7bf5fc15..bd37d69165f 100644
--- a/app/assets/javascripts/syntax_highlight.js
+++ b/app/assets/javascripts/syntax_highlight.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
// Syntax Highlighter
//
// Applies a syntax highlighting color scheme CSS class to any element with the
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js.es6 b/app/assets/javascripts/templates/issuable_template_selector.js.es6
index c32ddf80219..93a3d67ee9f 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js.es6
+++ b/app/assets/javascripts/templates/issuable_template_selector.js.es6
@@ -1,7 +1,8 @@
+/* eslint-disable */
/*= require ../blob/template_selector */
((global) => {
- class IssuableTemplateSelector extends TemplateSelector {
+ class IssuableTemplateSelector extends gl.TemplateSelector {
constructor(...args) {
super(...args);
this.projectPath = this.dropdown.data('project-path');
@@ -16,7 +17,13 @@
if (initialQuery.name) this.requestFile(initialQuery);
$('.reset-template', this.dropdown.parent()).on('click', () => {
- if (this.currentTemplate) this.setInputValueToTemplateContent();
+ this.setInputValueToTemplateContent();
+ });
+
+ $('.no-template', this.dropdown.parent()).on('click', () => {
+ this.currentTemplate = '';
+ this.setInputValueToTemplateContent();
+ $('.dropdown-toggle-text', this.dropdown).text('Choose a template');
});
}
@@ -36,16 +43,16 @@
// to the content of the template selected.
if (this.titleInput.val() === '') {
// If the title has not yet been set, focus the title input and
- // skip focusing the description input by setting `true` as the 2nd
- // argument to `requestFileSuccess`.
- this.requestFileSuccess(this.currentTemplate, true);
+ // skip focusing the description input by setting `true` as the
+ // `skipFocus` option to `requestFileSuccess`.
+ this.requestFileSuccess(this.currentTemplate, {skipFocus: true});
this.titleInput.focus();
} else {
- this.requestFileSuccess(this.currentTemplate);
+ this.requestFileSuccess(this.currentTemplate, {skipFocus: false});
}
return;
}
}
global.IssuableTemplateSelector = IssuableTemplateSelector;
-})(window);
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/templates/issuable_template_selectors.js.es6 b/app/assets/javascripts/templates/issuable_template_selectors.js.es6
index bd8cdde033e..0a3890e85fe 100644
--- a/app/assets/javascripts/templates/issuable_template_selectors.js.es6
+++ b/app/assets/javascripts/templates/issuable_template_selectors.js.es6
@@ -1,12 +1,13 @@
+/* eslint-disable */
((global) => {
class IssuableTemplateSelectors {
- constructor(opts = {}) {
- this.$dropdowns = opts.$dropdowns || $('.js-issuable-selector');
- this.editor = opts.editor || this.initEditor();
+ constructor({ $dropdowns, editor } = {}) {
+ this.$dropdowns = $dropdowns || $('.js-issuable-selector');
+ this.editor = editor || this.initEditor();
this.$dropdowns.each((i, dropdown) => {
- let $dropdown = $(dropdown);
- new IssuableTemplateSelector({
+ const $dropdown = $(dropdown);
+ new gl.IssuableTemplateSelector({
pattern: /(\.md)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-issuable-selector-wrap'),
@@ -26,4 +27,4 @@
}
global.IssuableTemplateSelectors = IssuableTemplateSelectors;
-})(window);
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js.es6
index 93421649ac7..213e80825b7 100644
--- a/app/assets/javascripts/todos.js
+++ b/app/assets/javascripts/todos.js.es6
@@ -1,34 +1,30 @@
-(function() {
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-
- this.Todos = (function() {
- function Todos(opts) {
- var ref;
- if (opts == null) {
- opts = {};
- }
- this.allDoneClicked = bind(this.allDoneClicked, this);
- this.doneClicked = bind(this.doneClicked, this);
- this.el = (ref = opts.el) != null ? ref : $('.js-todos-options');
+/* eslint-disable */
+((global) => {
+
+ class Todos {
+ constructor({ el } = {}) {
+ this.allDoneClicked = this.allDoneClicked.bind(this);
+ this.doneClicked = this.doneClicked.bind(this);
+ this.el = el || $('.js-todos-options');
this.perPage = this.el.data('perPage');
this.clearListeners();
this.initBtnListeners();
this.initFilters();
}
- Todos.prototype.clearListeners = function() {
+ clearListeners() {
$('.done-todo').off('click');
$('.js-todos-mark-all').off('click');
return $('.todo').off('click');
- };
+ }
- Todos.prototype.initBtnListeners = function() {
+ initBtnListeners() {
$('.done-todo').on('click', this.doneClicked);
$('.js-todos-mark-all').on('click', this.allDoneClicked);
return $('.todo').on('click', this.goToTodoUrl);
- };
+ }
- Todos.prototype.initFilters = function() {
+ initFilters() {
new UsersSelect();
this.initFilterDropdown($('.js-project-search'), 'project_id', ['text']);
this.initFilterDropdown($('.js-type-search'), 'type');
@@ -38,125 +34,118 @@
event.preventDefault();
Turbolinks.visit(this.action + '&' + $(this).serialize());
});
- };
+ }
- Todos.prototype.initFilterDropdown = function($dropdown, fieldName, searchFields) {
+ initFilterDropdown($dropdown, fieldName, searchFields) {
$dropdown.glDropdown({
+ fieldName,
selectable: true,
filterable: searchFields ? true : false,
- fieldName: fieldName,
search: { fields: searchFields },
data: $dropdown.data('data'),
clicked: function() {
return $dropdown.closest('form.filter-form').submit();
}
})
- };
+ }
- Todos.prototype.doneClicked = function(e) {
- var $this;
+ doneClicked(e) {
e.preventDefault();
e.stopImmediatePropagation();
- $this = $(e.currentTarget);
- $this.disable();
+ const $target = $(e.currentTarget);
+ $target.disable();
return $.ajax({
type: 'POST',
- url: $this.attr('href'),
+ url: $target.attr('href'),
dataType: 'json',
data: {
'_method': 'delete'
},
- success: (function(_this) {
- return function(data) {
- _this.redirectIfNeeded(data.count);
- _this.clearDone($this.closest('li'));
- return _this.updateBadges(data);
- };
- })(this)
+ success: (data) => {
+ this.redirectIfNeeded(data.count);
+ this.clearDone($target.closest('li'));
+ return this.updateBadges(data);
+ }
});
- };
+ }
- Todos.prototype.allDoneClicked = function(e) {
- var $this;
+ allDoneClicked(e) {
e.preventDefault();
e.stopImmediatePropagation();
- $this = $(e.currentTarget);
- $this.disable();
+ $target = $(e.currentTarget);
+ $target.disable();
return $.ajax({
type: 'POST',
- url: $this.attr('href'),
+ url: $target.attr('href'),
dataType: 'json',
data: {
'_method': 'delete'
},
- success: (function(_this) {
- return function(data) {
- $this.remove();
- $('.prepend-top-default').html('<div class="nothing-here-block">You\'re all done!</div>');
- return _this.updateBadges(data);
- };
- })(this)
+ success: (data) => {
+ $target.remove();
+ $('.prepend-top-default').html('<div class="nothing-here-block">You\'re all done!</div>');
+ return this.updateBadges(data);
+ }
});
- };
+ }
- Todos.prototype.clearDone = function($row) {
- var $ul;
- $ul = $row.closest('ul');
+ clearDone($row) {
+ const $ul = $row.closest('ul');
$row.remove();
if (!$ul.find('li').length) {
return $ul.parents('.panel').remove();
}
- };
+ }
- Todos.prototype.updateBadges = function(data) {
- $('.todos-pending .badge, .todos-pending-count').text(data.count);
+ updateBadges(data) {
+ $(document).trigger('todo:toggle', data.count);
+ $('.todos-pending .badge').text(data.count);
return $('.todos-done .badge').text(data.done_count);
- };
+ }
- Todos.prototype.getTotalPages = function() {
+ getTotalPages() {
return this.el.data('totalPages');
- };
+ }
- Todos.prototype.getCurrentPage = function() {
+ getCurrentPage() {
return this.el.data('currentPage');
- };
+ }
- Todos.prototype.getTodosPerPage = function() {
+ getTodosPerPage() {
return this.el.data('perPage');
- };
+ }
+
+ redirectIfNeeded(total) {
+ const currPages = this.getTotalPages();
+ const currPage = this.getCurrentPage();
- Todos.prototype.redirectIfNeeded = function(total) {
- var currPage, currPages, newPages, pageParams, url;
- currPages = this.getTotalPages();
- currPage = this.getCurrentPage();
// Refresh if no remaining Todos
if (!total) {
- location.reload();
+ window.location.reload();
return;
}
// Do nothing if no pagination
if (!currPages) {
return;
}
- newPages = Math.ceil(total / this.getTodosPerPage());
- // Includes query strings
- url = location.href;
- // If new total of pages is different than we have now
+
+ const newPages = Math.ceil(total / this.getTodosPerPage());
+ let url = location.href;
+
if (newPages !== currPages) {
// Redirect to previous page if there's one available
if (currPages > 1 && currPage === currPages) {
- pageParams = {
+ const pageParams = {
page: currPages - 1
};
url = gl.utils.mergeUrlParams(pageParams, url);
}
return Turbolinks.visit(url);
}
- };
+ }
- Todos.prototype.goToTodoUrl = function(e) {
- var todoLink;
- todoLink = $(this).data('url');
+ goToTodoUrl(e) {
+ const todoLink = $(this).data('url');
if (!todoLink) {
return;
}
@@ -167,10 +156,8 @@
} else {
return Turbolinks.visit(todoLink);
}
- };
-
- return Todos;
-
- })();
+ }
+ }
-}).call(this);
+ global.Todos = Todos;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js
index 9b7be17c4fe..f48a7ee0f55 100644
--- a/app/assets/javascripts/tree.js
+++ b/app/assets/javascripts/tree.js
@@ -1,3 +1,5 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
+/* global Turbolinks */
(function() {
this.TreeView = (function() {
function TreeView() {
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index ce2930c7fc7..5d991542b51 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, prefer-arrow-callback, no-undef, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js
index bc48c67c4f2..4c70a6e9bb6 100644
--- a/app/assets/javascripts/u2f/error.js
+++ b/app/assets/javascripts/u2f/error.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-console, quotes, prefer-template, no-undef, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js
index 926912fa988..97d8993cac2 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-undef, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */
// Register U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> registered -> POST to server
diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js
index 907e640161a..eedd3bcd5a1 100644
--- a/app/assets/javascripts/u2f/util.js
+++ b/app/assets/javascripts/u2f/util.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, padded-blocks */
(function() {
this.U2FUtil = (function() {
function U2FUtil() {}
diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6
index 6889d3a7491..5e869e99fdb 100644
--- a/app/assets/javascripts/user.js.es6
+++ b/app/assets/javascripts/user.js.es6
@@ -1,7 +1,8 @@
-(global => {
+/* eslint-disable */
+((global) => {
global.User = class {
- constructor(opts) {
- this.opts = opts;
+ constructor({ action }) {
+ this.action = action;
this.placeProfileAvatarsToTop();
this.initTabs();
this.hideProjectLimitMessage();
@@ -14,19 +15,16 @@
}
initTabs() {
- return new UserTabs({
+ return new global.UserTabs({
parentEl: '.user-profile',
- action: this.opts.action
+ action: this.action
});
}
hideProjectLimitMessage() {
$('.hide-project-limit-message').on('click', e => {
e.preventDefault();
- const path = gon.relative_url_root || '/';
- $.cookie('hide_project_limit_message', 'false', {
- path: path
- });
+ Cookies.set('hide_project_limit_message', 'false');
$(this).parents('.project-limit-message').remove();
});
}
diff --git a/app/assets/javascripts/user_tabs.js b/app/assets/javascripts/user_tabs.js
deleted file mode 100644
index 8a657780eb6..00000000000
--- a/app/assets/javascripts/user_tabs.js
+++ /dev/null
@@ -1,188 +0,0 @@
-// UserTabs
-//
-// Handles persisting and restoring the current tab selection and lazily-loading
-// content on the Users#show page.
-//
-// ### Example Markup
-//
-// <ul class="nav-links">
-// <li class="activity-tab active">
-// <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
-// Activity
-// </a>
-// </li>
-// <li class="groups-tab">
-// <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
-// Groups
-// </a>
-// </li>
-// <li class="contributed-tab">
-// <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed">
-// Contributed projects
-// </a>
-// </li>
-// <li class="projects-tab">
-// <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects">
-// Personal projects
-// </a>
-// </li>
-// <li class="snippets-tab">
-// <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets">
-// </a>
-// </li>
-// </ul>
-//
-// <div class="tab-content">
-// <div class="tab-pane" id="activity">
-// Activity Content
-// </div>
-// <div class="tab-pane" id="groups">
-// Groups Content
-// </div>
-// <div class="tab-pane" id="contributed">
-// Contributed projects content
-// </div>
-// <div class="tab-pane" id="projects">
-// Projects content
-// </div>
-// <div class="tab-pane" id="snippets">
-// Snippets content
-// </div>
-// </div>
-//
-// <div class="loading-status">
-// <div class="loading">
-// Loading Animation
-// </div>
-// </div>
-//
-(function() {
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-
- this.UserTabs = (function() {
- function UserTabs(opts) {
- this.tabShown = bind(this.tabShown, this);
- var i, item, len, ref, ref1, ref2, ref3;
- this.action = (ref = opts.action) != null ? ref : 'activity', this.defaultAction = (ref1 = opts.defaultAction) != null ? ref1 : 'activity', this.parentEl = (ref2 = opts.parentEl) != null ? ref2 : $(document);
- // Make jQuery object if selector is provided
- if (typeof this.parentEl === 'string') {
- this.parentEl = $(this.parentEl);
- }
- // Store the `location` object, allowing for easier stubbing in tests
- this._location = location;
- // Set tab states
- this.loaded = {};
- ref3 = this.parentEl.find('.nav-links a');
- for (i = 0, len = ref3.length; i < len; i++) {
- item = ref3[i];
- this.loaded[$(item).attr('data-action')] = false;
- }
- // Actions
- this.actions = Object.keys(this.loaded);
- this.bindEvents();
- // Set active tab
- if (this.action === 'show') {
- this.action = this.defaultAction;
- }
- this.activateTab(this.action);
- }
-
- UserTabs.prototype.bindEvents = function() {
- // Toggle event listeners
- return this.parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]').on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', this.tabShown);
- };
-
- UserTabs.prototype.tabShown = function(event) {
- var $target, action, source;
- $target = $(event.target);
- action = $target.data('action');
- source = $target.attr('href');
- this.setTab(source, action);
- return this.setCurrentAction(action);
- };
-
- UserTabs.prototype.activateTab = function(action) {
- return this.parentEl.find(".nav-links .js-" + action + "-tab a").tab('show');
- };
-
- UserTabs.prototype.setTab = function(source, action) {
- if (this.loaded[action] === true) {
- return;
- }
- if (action === 'activity') {
- this.loadActivities(source);
- }
- if (action === 'groups' || action === 'contributed' || action === 'projects' || action === 'snippets') {
- return this.loadTab(source, action);
- }
- };
-
- UserTabs.prototype.loadTab = function(source, action) {
- return $.ajax({
- beforeSend: (function(_this) {
- return function() {
- return _this.toggleLoading(true);
- };
- })(this),
- complete: (function(_this) {
- return function() {
- return _this.toggleLoading(false);
- };
- })(this),
- dataType: 'json',
- type: 'GET',
- url: source + ".json",
- success: (function(_this) {
- return function(data) {
- var tabSelector;
- tabSelector = 'div#' + action;
- _this.parentEl.find(tabSelector).html(data.html);
- _this.loaded[action] = true;
- // Fix tooltips
- return gl.utils.localTimeAgo($('.js-timeago', tabSelector));
- };
- })(this)
- });
- };
-
- UserTabs.prototype.loadActivities = function(source) {
- var $calendarWrap;
- if (this.loaded['activity'] === true) {
- return;
- }
- $calendarWrap = this.parentEl.find('.user-calendar');
- $calendarWrap.load($calendarWrap.data('href'));
- new Activities();
- return this.loaded['activity'] = true;
- };
-
- UserTabs.prototype.toggleLoading = function(status) {
- return this.parentEl.find('.loading-status .loading').toggle(status);
- };
-
- UserTabs.prototype.setCurrentAction = function(action) {
- var new_state, regExp;
- // Remove possible actions from URL
- regExp = new RegExp('\/(' + this.actions.join('|') + ')(\.html)?\/?$');
- new_state = this._location.pathname;
- // remove trailing slashes
- new_state = new_state.replace(/\/+$/, "");
- new_state = new_state.replace(regExp, '');
- // Append the new action if we're on a tab other than 'activity'
- if (action !== this.defaultAction) {
- new_state += "/" + action;
- }
- // Ensure parameters and hash come along for the ride
- new_state += this._location.search + this._location.hash;
- history.replaceState({
- turbolinks: true,
- url: new_state
- }, document.title, new_state);
- return new_state;
- };
-
- return UserTabs;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/user_tabs.js.es6 b/app/assets/javascripts/user_tabs.js.es6
new file mode 100644
index 00000000000..5a625611987
--- /dev/null
+++ b/app/assets/javascripts/user_tabs.js.es6
@@ -0,0 +1,158 @@
+/* eslint-disable */
+/*
+UserTabs
+
+Handles persisting and restoring the current tab selection and lazily-loading
+content on the Users#show page.
+
+### Example Markup
+
+ <ul class="nav-links">
+ <li class="activity-tab active">
+ <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
+ Activity
+ </a>
+ </li>
+ <li class="groups-tab">
+ <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
+ Groups
+ </a>
+ </li>
+ <li class="contributed-tab">
+ <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed">
+ Contributed projects
+ </a>
+ </li>
+ <li class="projects-tab">
+ <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects">
+ Personal projects
+ </a>
+ </li>
+ <li class="snippets-tab">
+ <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets">
+ </a>
+ </li>
+ </ul>
+
+ <div class="tab-content">
+ <div class="tab-pane" id="activity">
+ Activity Content
+ </div>
+ <div class="tab-pane" id="groups">
+ Groups Content
+ </div>
+ <div class="tab-pane" id="contributed">
+ Contributed projects content
+ </div>
+ <div class="tab-pane" id="projects">
+ Projects content
+ </div>
+ <div class="tab-pane" id="snippets">
+ Snippets content
+ </div>
+ </div>
+
+ <div class="loading-status">
+ <div class="loading">
+ Loading Animation
+ </div>
+ </div>
+*/
+((global) => {
+ class UserTabs {
+ constructor ({ defaultAction, action, parentEl }) {
+ this.loaded = {};
+ this.defaultAction = defaultAction || 'activity';
+ this.action = action || this.defaultAction;
+ this.$parentEl = $(parentEl) || $(document);
+ this._location = window.location;
+ this.$parentEl.find('.nav-links a')
+ .each((i, navLink) => {
+ this.loaded[$(navLink).attr('data-action')] = false;
+ });
+ this.actions = Object.keys(this.loaded);
+ this.bindEvents();
+
+ if (this.action === 'show') {
+ this.action = this.defaultAction;
+ }
+
+ this.activateTab(this.action);
+ }
+
+ bindEvents() {
+ return 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');
+ const source = $target.attr('href');
+ this.setTab(source, action);
+ return this.setCurrentAction(source, action);
+ }
+
+ activateTab(action) {
+ return this.$parentEl.find(`.nav-links .js-${action}-tab a`)
+ .tab('show');
+ }
+
+ setTab(source, action) {
+ if (this.loaded[action]) {
+ return;
+ }
+ if (action === 'activity') {
+ this.loadActivities(source);
+ }
+
+ const loadableActions = [ 'groups', 'contributed', 'projects', 'snippets' ];
+ if (loadableActions.indexOf(action) > -1) {
+ return this.loadTab(source, action);
+ }
+ }
+
+ loadTab(source, action) {
+ return $.ajax({
+ beforeSend: () => this.toggleLoading(true),
+ complete: () => this.toggleLoading(false),
+ dataType: 'json',
+ type: 'GET',
+ url: `${source}.json`,
+ success: (data) => {
+ const tabSelector = `div#${action}`;
+ this.$parentEl.find(tabSelector).html(data.html);
+ this.loaded[action] = true;
+ return gl.utils.localTimeAgo($('.js-timeago', tabSelector));
+ }
+ });
+ }
+
+ loadActivities(source) {
+ if (this.loaded['activity']) {
+ return;
+ }
+ const $calendarWrap = this.$parentEl.find('.user-calendar');
+ $calendarWrap.load($calendarWrap.data('href'));
+ new gl.Activities();
+ return this.loaded['activity'] = true;
+ }
+
+ toggleLoading(status) {
+ return this.$parentEl.find('.loading-status .loading')
+ .toggle(status);
+ }
+
+ setCurrentAction(source, action) {
+ let new_state = source
+ new_state = new_state.replace(/\/+$/, '');
+ new_state += this._location.search + this._location.hash;
+ history.replaceState({
+ turbolinks: true,
+ url: new_state
+ }, document.title, new_state);
+ return new_state;
+ }
+ }
+ global.UserTabs = UserTabs;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/username_validator.js.es6 b/app/assets/javascripts/username_validator.js.es6
new file mode 100644
index 00000000000..c4dde575c6e
--- /dev/null
+++ b/app/assets/javascripts/username_validator.js.es6
@@ -0,0 +1,134 @@
+/* eslint-disable */
+((global) => {
+ const debounceTimeoutDuration = 1000;
+ const invalidInputClass = 'gl-field-error-outline';
+ const successInputClass = 'gl-field-success-outline';
+ const unavailableMessageSelector = '.username .validation-error';
+ const successMessageSelector = '.username .validation-success';
+ const pendingMessageSelector = '.username .validation-pending';
+ const invalidMessageSelector = '.username .gl-field-error';
+
+ class UsernameValidator {
+ constructor() {
+ this.inputElement = $('#new_user_username');
+ this.inputDomElement = this.inputElement.get(0);
+ this.state = {
+ available: false,
+ valid: false,
+ pending: false,
+ empty: true
+ };
+
+ const debounceTimeout = _.debounce((username) => {
+ this.validateUsername(username);
+ }, debounceTimeoutDuration);
+
+ this.inputElement.on('keyup.username_check', () => {
+ const username = this.inputElement.val();
+
+ this.state.valid = this.inputDomElement.validity.valid;
+ this.state.empty = !username.length;
+
+ if (this.state.valid) {
+ return debounceTimeout(username);
+ }
+
+ this.renderState();
+ });
+
+ // Override generic field validation
+ this.inputElement.on('invalid', this.interceptInvalid.bind(this));
+ }
+
+ renderState() {
+ // Clear all state
+ this.clearFieldValidationState();
+
+ if (this.state.valid && this.state.available) {
+ return this.setSuccessState();
+ }
+
+ if (this.state.empty) {
+ return this.clearFieldValidationState();
+ }
+
+ if (this.state.pending) {
+ return this.setPendingState();
+ }
+
+ if (!this.state.available) {
+ return this.setUnavailableState();
+ }
+
+ if (!this.state.valid) {
+ return this.setInvalidState();
+ }
+ }
+
+ interceptInvalid(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ validateUsername(username) {
+ if (this.state.valid) {
+ this.state.pending = true;
+ this.state.available = false;
+ this.renderState();
+ return $.ajax({
+ type: 'GET',
+ url: `/users/${username}/exists`,
+ dataType: 'json',
+ success: (res) => this.setAvailabilityState(res.exists)
+ });
+ }
+ }
+
+ setAvailabilityState(usernameTaken) {
+ if (usernameTaken) {
+ this.state.valid = false;
+ this.state.available = false;
+ } else {
+ this.state.available = true;
+ }
+ this.state.pending = false;
+ this.renderState();
+ }
+
+ clearFieldValidationState() {
+ this.inputElement.siblings('p').hide();
+
+ this.inputElement.removeClass(invalidInputClass)
+ .removeClass(successInputClass);
+ }
+
+ setUnavailableState() {
+ const $usernameUnavailableMessage = this.inputElement.siblings(unavailableMessageSelector);
+ this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
+ $usernameUnavailableMessage.show();
+ }
+
+ setSuccessState() {
+ const $usernameSuccessMessage = this.inputElement.siblings(successMessageSelector);
+ this.inputElement.addClass(successInputClass).removeClass(invalidInputClass);
+ $usernameSuccessMessage.show();
+ }
+
+ setPendingState() {
+ const $usernamePendingMessage = $(pendingMessageSelector);
+ if (this.state.pending) {
+ $usernamePendingMessage.show();
+ } else {
+ $usernamePendingMessage.hide();
+ }
+ }
+
+ setInvalidState() {
+ const $inputErrorMessage = $(invalidMessageSelector);
+ this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
+ $inputErrorMessage.show();
+ }
+ }
+
+ global.UsernameValidator = UsernameValidator;
+})(window);
diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js
index b8da7c4f297..ba7f533c349 100644
--- a/app/assets/javascripts/users/calendar.js
+++ b/app/assets/javascripts/users/calendar.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, camelcase, vars-on-top, semi, keyword-spacing, no-plusplus, no-undef, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -29,7 +30,7 @@
date.setDate(date.getDate() + i);
var day = date.getDay();
- var count = timestamps[date.getTime() * 0.001];
+ var count = timestamps[dateFormat(date, 'yyyy-mm-dd')];
// Create a new group array if this is the first day of the week
// or if is first object
@@ -205,6 +206,7 @@
}
});
} else {
+ this.currentSelectedDate = '';
return $('.user-calendar-activities').html('');
}
};
diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js
index d6e4d9f7ad8..f50802bdf2e 100644
--- a/app/assets/javascripts/users/users_bundle.js
+++ b/app/assets/javascripts/users/users_bundle.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren */
/*= require_tree . */
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 9c277998db4..c6e18fad832 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, one-var, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, no-undef, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-plusplus, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, keyword-spacing, no-param-reassign, padded-blocks, max-len */
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
slice = [].slice;
@@ -9,16 +10,23 @@
this.usersPath = "/autocomplete/users.json";
this.userPath = "/autocomplete/users/:id.json";
if (currentUser != null) {
- this.currentUser = JSON.parse(currentUser);
+ if (typeof currentUser === 'object') {
+ this.currentUser = currentUser;
+ } else {
+ this.currentUser = JSON.parse(currentUser);
+ }
}
$('.js-user-search').each((function(_this) {
return function(i, dropdown) {
var options = {};
- var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser;
+ var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser, showMenuAbove;
$dropdown = $(dropdown);
options.projectId = $dropdown.data('project-id');
options.showCurrentUser = $dropdown.data('current-user');
+ options.todoFilter = $dropdown.data('todo-filter');
+ options.todoStateFilter = $dropdown.data('todo-state-filter');
showNullUser = $dropdown.data('null-user');
+ showMenuAbove = $dropdown.data('showMenuAbove');
showAnyUser = $dropdown.data('any-user');
firstUser = $dropdown.data('first-user');
options.authorId = $dropdown.data('author-id');
@@ -31,9 +39,30 @@
$value = $block.find('.value');
$collapsedSidebar = $block.find('.sidebar-collapsed-user');
$loading = $block.find('.block-loading').fadeOut();
+
+ var updateIssueBoardsIssue = function () {
+ $loading.fadeIn();
+ gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
+ .then(function () {
+ $loading.fadeOut();
+ });
+ };
+
$block.on('click', '.js-assign-yourself', function(e) {
e.preventDefault();
- return assignTo(_this.currentUser.id);
+
+ if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'assignee', new ListUser({
+ id: _this.currentUser.id,
+ username: _this.currentUser.username,
+ name: _this.currentUser.name,
+ avatar_url: _this.currentUser.avatar_url
+ }));
+
+ updateIssueBoardsIssue();
+ } else {
+ return assignTo(_this.currentUser.id);
+ }
});
assignTo = function(selected) {
var data;
@@ -70,9 +99,10 @@
return $collapsedSidebar.html(collapsedAssigneeTemplate(user));
});
};
- collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <a class="author_link" href="/u/<%- 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="/u/<%- 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> <% } %>');
+ 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) {
var isAuthorFilter;
isAuthorFilter = $('.js-author-search');
@@ -116,8 +146,11 @@
if (showDivider) {
users.splice(showDivider, 0, "divider");
}
- // Send the data back
- return callback(users);
+
+ callback(users);
+ if (showMenuAbove) {
+ $dropdown.data('glDropdown').positionMenuAbove();
+ }
});
},
filterable: true,
@@ -127,8 +160,8 @@
},
selectable: true,
fieldName: $dropdown.data('field-name'),
- toggleLabel: function(selected) {
- if (selected && 'id' in selected) {
+ toggleLabel: function(selected, el) {
+ if (selected && 'id' in selected && $(el).hasClass('is-active')) {
if (selected.text) {
return selected.text;
} else {
@@ -138,21 +171,25 @@
return defaultLabel;
}
},
+ defaultLabel: defaultLabel,
inputId: 'issue_assignee_id',
hidden: function(e) {
$selectbox.hide();
// display:block overrides the hide-collapse rule
return $value.css('display', '');
},
+ vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(user, $el, e) {
var isIssueIndex, isMRIndex, page, selected;
page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index';
isMRIndex = (page === page && page === 'projects:merge_requests:index');
- if ($dropdown.hasClass('js-filter-bulk-update')) {
+ if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
+ e.preventDefault();
+ selectedId = user.id;
return;
}
- if (page === 'projects:boards:show') {
+ if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar')) {
selectedId = user.id;
gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = user.id;
gl.issueBoards.BoardsStore.updateFiltersUrl();
@@ -162,11 +199,27 @@
return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
+ } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
+ if (user.id) {
+ Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'assignee', new ListUser({
+ id: user.id,
+ username: user.username,
+ name: user.name,
+ avatar_url: user.avatar_url
+ }));
+ } else {
+ Vue.delete(gl.issueBoards.BoardsStore.detail.issue, 'assignee');
+ }
+
+ updateIssueBoardsIssue();
} else {
selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val();
return assignTo(selected);
}
},
+ id: function (user) {
+ return user.id;
+ },
renderRow: function(user) {
var avatar, img, listClosingTags, listWithName, listWithUserName, selected, username;
username = user.username ? "@" + user.username : "";
@@ -250,10 +303,11 @@
}
}
if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) {
+ var trimmed = query.term.trim();
emailUser = {
name: "Invite \"" + query.term + "\"",
- username: query.term,
- id: query.term
+ username: trimmed,
+ id: trimmed
};
data.results.unshift(emailUser);
}
@@ -313,6 +367,10 @@
};
UsersSelect.prototype.user = function(user_id, callback) {
+ if(!/^\d+$/.test(user_id)) {
+ return false;
+ }
+
var url;
url = this.buildUrl(this.userPath);
url = url.replace(':id', user_id);
@@ -338,6 +396,8 @@
project_id: options.projectId || null,
group_id: options.groupId || null,
skip_ldap: options.skipLdap || null,
+ todo_filter: options.todoFilter || null,
+ todo_state_filter: options.todoStateFilter || null,
current_user: options.showCurrentUser || null,
push_code_to_protected_branches: options.pushCodeToProtectedBranches || null,
author_id: options.authorId || null,
diff --git a/app/assets/javascripts/vue_common_component/commit.js.es6 b/app/assets/javascripts/vue_common_component/commit.js.es6
new file mode 100644
index 00000000000..1bc68c1ba2f
--- /dev/null
+++ b/app/assets/javascripts/vue_common_component/commit.js.es6
@@ -0,0 +1,176 @@
+/*= require vue */
+/* global Vue */
+(() => {
+ window.gl = window.gl || {};
+
+ window.gl.CommitComponent = Vue.component('commit-component', {
+
+ props: {
+ /**
+ * Indicates the existance of a tag.
+ * Used to render the correct icon, if true will render `fa-tag` icon,
+ * if false will render `fa-code-fork` icon.
+ */
+ tag: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+
+ /**
+ * If provided is used to render the branch name and url.
+ * Should contain the following properties:
+ * name
+ * ref_url
+ */
+ ref: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+
+ /**
+ * Used to link to the commit sha.
+ */
+ commit_url: {
+ type: String,
+ required: false,
+ default: '',
+ },
+
+ /**
+ * Used to show the commit short_sha that links to the commit url.
+ */
+ short_sha: {
+ type: String,
+ required: false,
+ default: '',
+ },
+
+ /**
+ * If provided shows the commit tile.
+ */
+ title: {
+ type: String,
+ required: false,
+ default: '',
+ },
+
+ /**
+ * If provided renders information about the author of the commit.
+ * When provided should include:
+ * `avatar_url` to render the avatar icon
+ * `web_url` to link to user profile
+ * `username` to render alt and title tags
+ */
+ author: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+
+ computed: {
+ /**
+ * Used to verify if all the properties needed to render the commit
+ * ref section were provided.
+ *
+ * TODO: Improve this! Use lodash _.has when we have it.
+ *
+ * @returns {Boolean}
+ */
+ hasRef() {
+ return this.ref && this.ref.name && this.ref.ref_url;
+ },
+
+ /**
+ * Used to verify if all the properties needed to render the commit
+ * author section were provided.
+ *
+ * TODO: Improve this! Use lodash _.has when we have it.
+ *
+ * @returns {Boolean}
+ */
+ hasAuthor() {
+ return this.author &&
+ this.author.avatar_url &&
+ this.author.web_url &&
+ this.author.username;
+ },
+
+ /**
+ * If information about the author is provided will return a string
+ * to be rendered as the alt attribute of the img tag.
+ *
+ * @returns {String}
+ */
+ userImageAltDescription() {
+ return this.author &&
+ this.author.username ? `${this.author.username}'s avatar` : null;
+ },
+ },
+
+ /**
+ * In order to reuse the svg instead of copy and paste in this template
+ * we need to render it outside this component using =custom_icon partial.
+ * Make sure it has this structure:
+ * .commit-icon-svg.hidden
+ * svg
+ *
+ * TODO: Find a better way to include SVG
+ */
+ mounted() {
+ const commitIconContainer = this.$el.querySelector('.commit-icon-container');
+ const commitIcon = document.querySelector('.commit-icon-svg.hidden svg');
+
+ if (commitIconContainer && commitIcon) {
+ commitIconContainer.appendChild(commitIcon.cloneNode(true));
+ }
+ },
+
+ template: `
+ <div class="branch-commit">
+
+ <div v-if="hasRef" class="icon-container">
+ <i v-if="tag" class="fa fa-tag"></i>
+ <i v-if="!tag" class="fa fa-code-fork"></i>
+ </div>
+
+ <a v-if="hasRef"
+ class="monospace branch-name"
+ :href="ref.ref_url">
+ {{ref.name}}
+ </a>
+
+ <div class="icon-container commit-icon commit-icon-container"></div>
+
+ <a class="commit-id monospace"
+ :href="commit_url">
+ {{short_sha}}
+ </a>
+
+ <p class="commit-title">
+ <span v-if="title">
+ <a v-if="hasAuthor"
+ class="avatar-image-container"
+ :href="author.web_url">
+ <img
+ class="avatar has-tooltip s20"
+ :src="author.avatar_url"
+ :alt="userImageAltDescription"
+ :title="author.username" />
+ </a>
+
+ <a class="commit-row-message"
+ :href="commit_url">
+ {{title}}
+ </a>
+ </span>
+ <span v-else>
+ Cant find HEAD commit for this branch
+ </span>
+ </p>
+ </div>
+ `,
+ });
+})();
diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/wikis.js
index 35401231fbf..5dd853389c2 100644
--- a/app/assets/javascripts/wikis.js
+++ b/app/assets/javascripts/wikis.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, consistent-return, one-var, one-var-declaration-per-line, no-undef, prefer-template, padded-blocks, max-len */
/*= require latinise */
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index 777b32b41c9..82eb761442a 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -1,3 +1,4 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, no-undef, camelcase, comma-dangle, padded-blocks, max-len */
// Zen Mode (full screen) textarea
//
/*= provides zen_mode:enter */
diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss
index 897bc49e7df..e3ca7f6373a 100644
--- a/app/assets/stylesheets/behaviors.scss
+++ b/app/assets/stylesheets/behaviors.scss
@@ -5,6 +5,7 @@
display: none;
&.hide { display: block; }
}
+
&.open .content {
display: block;
&.hide { display: none; }
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index d5cca1b10fb..7c7f991dd87 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -39,3 +39,5 @@
@import "framework/typography.scss";
@import "framework/zen.scss";
@import "framework/blank";
+@import "framework/wells.scss";
+@import "framework/page-header.scss";
diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss
index 1e9a45c19b8..f1d36efb3de 100644
--- a/app/assets/stylesheets/framework/animations.scss
+++ b/app/assets/stylesheets/framework/animations.scss
@@ -1,10 +1,10 @@
// This file is based off animate.css 3.5.1, available here:
// https://github.com/daneden/animate.css/blob/3.5.1/animate.css
-//
+//
// animate.css - http://daneden.me/animate
// Version - 3.5.1
// Licensed under the MIT license - http://opensource.org/licenses/MIT
-//
+//
// Copyright (c) 2016 Daniel Eden
.animated {
@@ -37,7 +37,8 @@
}
@include keyframes(pulse) {
- from, to {
+ from,
+ to {
@include webkit-prefix(transform, scale3d(1, 1, 1));
}
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index c79b22d4d21..ad0d387067f 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -1,14 +1,40 @@
-.avatar {
+@mixin avatar-size($size, $margin-right) {
+ width: $size;
+ height: $size;
+ margin-right: $margin-right;
+}
+
+.avatar-circle {
float: left;
- margin-right: 12px;
+ margin-right: 15px;
+ border-radius: $avatar_radius;
+ border: 1px solid rgba(0, 0, 0, .1);
+ &.s16 { @include avatar-size(16px, 6px); }
+ &.s20 { @include avatar-size(20px, 7px); }
+ &.s24 { @include avatar-size(24px, 8px); }
+ &.s26 { @include avatar-size(26px, 8px); }
+ &.s32 { @include avatar-size(32px, 10px); }
+ &.s36 { @include avatar-size(36px, 10px); }
+ &.s40 { @include avatar-size(40px, 10px); }
+ &.s46 { @include avatar-size(46px, 15px); }
+ &.s48 { @include avatar-size(48px, 10px); }
+ &.s60 { @include avatar-size(60px, 12px); }
+ &.s70 { @include avatar-size(70px, 14px); }
+ &.s90 { @include avatar-size(90px, 15px); }
+ &.s110 { @include avatar-size(110px, 15px); }
+ &.s140 { @include avatar-size(140px, 15px); }
+ &.s160 { @include avatar-size(160px, 20px); }
+}
+
+.avatar {
+ @extend .avatar-circle;
width: 40px;
height: 40px;
padding: 0;
- @include border-radius($avatar_radius);
- border: 1px solid rgba(0, 0, 0, .1);
&.avatar-inline {
float: none;
+ display: inline-block;
margin-left: 4px;
margin-bottom: 2px;
@@ -16,26 +42,16 @@
&.s24 { margin-right: 4px; }
}
+ &.center {
+ font-size: 14px;
+ line-height: 1.8em;
+ text-align: center;
+ }
+
&.avatar-tile {
- @include border-radius(0);
+ border-radius: 0;
border: none;
}
-
- &.s16 { width: 16px; height: 16px; margin-right: 6px; }
- &.s20 { width: 20px; height: 20px; margin-right: 7px; }
- &.s24 { width: 24px; height: 24px; margin-right: 8px; }
- &.s26 { width: 26px; height: 26px; margin-right: 8px; }
- &.s32 { width: 32px; height: 32px; margin-right: 10px; }
- &.s36 { width: 36px; height: 36px; margin-right: 10px; }
- &.s40 { width: 40px; height: 40px; margin-right: 10px; }
- &.s46 { width: 46px; height: 46px; margin-right: 15px; }
- &.s48 { width: 48px; height: 48px; margin-right: 10px; }
- &.s60 { width: 60px; height: 60px; margin-right: 12px; }
- &.s70 { width: 70px; height: 70px; margin-right: 14px; }
- &.s90 { width: 90px; height: 90px; margin-right: 15px; }
- &.s110 { width: 110px; height: 110px; margin-right: 15px; }
- &.s140 { width: 140px; height: 140px; margin-right: 20px; }
- &.s160 { width: 160px; height: 160px; margin-right: 20px; }
}
.identicon {
@@ -54,3 +70,17 @@
&.s140 { font-size: 72px; line-height: 138px; }
&.s160 { font-size: 96px; line-height: 158px; }
}
+
+.avatar-container {
+ @extend .avatar-circle;
+ overflow: hidden;
+ display: flex;
+
+ .avatar {
+ border-radius: 0;
+ border: none;
+ height: auto;
+ margin: 0;
+ align-self: center;
+ }
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index f5223207f3a..77ae9e9a6e7 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -19,10 +19,9 @@
&.diff-collapsed {
padding: 5px;
- cursor: pointer;
- &:hover {
- background-color: $row-hover;
+ .click-to-expand {
+ cursor: pointer;
}
}
}
@@ -129,27 +128,20 @@
position: relative;
.avatar-holder {
- margin-bottom: 16px;
-
- .avatar, .identicon {
+ .avatar,
+ .identicon {
margin: 0 auto;
float: none;
}
.identicon {
- @include border-radius(50%);
+ border-radius: 50%;
}
}
.cover-title {
color: $gl-header-color;
- margin: 0;
- font-size: 24px;
- font-weight: normal;
- margin-bottom: 10px;
- color: #4c4e54;
font-size: 23px;
- line-height: 1.1;
h1 {
color: $gl-gray-dark;
@@ -214,6 +206,10 @@
}
}
+ &.user-cover-block {
+ padding: 24px 0 0;
+ }
+
.group-info {
h1 {
@@ -258,3 +254,32 @@
.content-block-small {
padding: 10px 0;
}
+
+.empty-state {
+ margin: 100px 0 0;
+
+ .text-content {
+ max-width: 460px;
+ margin: 0 auto;
+ padding: $gl-padding;
+ }
+
+ .svg-content {
+ text-align: center;
+
+ svg {
+ max-width: 425px;
+ width: 100%;
+ padding: $gl-padding;
+ }
+ }
+
+ @media(max-width: $screen-xs-max) {
+ margin-top: 50px;
+ text-align: center;
+
+ .btn {
+ width: 100%;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 4618687a4be..4a9aa0f8717 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -1,14 +1,13 @@
@mixin btn-default {
- @include border-radius(3px);
+ border-radius: 3px;
font-size: $gl-font-size;
font-weight: 500;
padding: $gl-vert-padding $gl-btn-padding;
&:focus,
&:active {
- outline: none;
background-color: $btn-active-gray;
- @include box-shadow($gl-btn-active-background);
+ box-shadow: $gl-btn-active-background;
}
}
@@ -25,7 +24,7 @@
&:focus {
background-color: $hover-background;
color: $hover-text;
- border-color: $hover-border;;
+ border-color: $hover-border;
}
}
@@ -43,7 +42,7 @@
&:active,
&.active {
- @include box-shadow ($gl-btn-active-background);
+ box-shadow: $gl-btn-active-background;
background-color: $dark;
border-color: $border-dark;
@@ -142,6 +141,10 @@
&.btn-save {
@include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light);
}
+
+ &.btn-remove {
+ @include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
+ }
}
&.btn-gray {
@@ -152,7 +155,8 @@
@include btn-blue-medium;
}
- &.btn-info {
+ &.btn-info,
+ &.btn-register {
@include btn-blue;
}
@@ -194,10 +198,17 @@
pointer-events: none !important;
}
- .caret {
+ .fa-caret-down,
+ .fa-caret-up {
margin-left: 5px;
}
+ &.dropdown-toggle {
+ .fa-caret-down {
+ margin-left: 3px;
+ }
+ }
+
svg {
height: 15px;
width: 15px;
@@ -205,9 +216,10 @@
top: 2px;
}
- svg, .fa {
+ svg,
+ .fa {
&:not(:last-child) {
- margin-right: 3px;
+ margin-right: 5px;
}
}
}
@@ -233,6 +245,7 @@
width: 100%;
margin: 0;
margin-bottom: 15px;
+
&.btn {
padding: 6px 0;
}
@@ -257,10 +270,6 @@
outline: none;
}
- &:focus {
- outline: none;
- }
-
&:active {
outline: none;
}
@@ -272,7 +281,7 @@
}
.active {
- @include box-shadow($gl-btn-active-background);
+ box-shadow: $gl-btn-active-background;
border: 1px solid #c6cacf !important;
background-color: #e4e7ed !important;
@@ -314,6 +323,7 @@
.btn-build {
margin-left: 10px;
+
i {
color: $gl-icon-color;
}
@@ -321,6 +331,7 @@
.clone-dropdown-btn a {
color: $dropdown-link-color;
+
&:hover {
text-decoration: none;
}
@@ -330,9 +341,22 @@
background-color: $background-color !important;
border: 1px solid lightgrey;
cursor: default;
+
&:active {
-moz-box-shadow: inset 0 0 0 white;
-webkit-box-shadow: inset 0 0 0 white;
box-shadow: inset 0 0 0 white;
}
}
+
+.btn-inverted {
+ &-secondary {
+ @include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light);
+ }
+}
+
+@media (max-width: $screen-xs-max) {
+ .btn-wide-on-xs {
+ width: 100%;
+ }
+}
diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss
index da7bab74a32..f3b6ad88ad6 100644
--- a/app/assets/stylesheets/framework/callout.scss
+++ b/app/assets/stylesheets/framework/callout.scss
@@ -13,10 +13,12 @@
color: $text-color;
background: $background-color;
}
+
.bs-callout h4 {
margin-top: 0;
margin-bottom: 5px;
}
+
.bs-callout p:last-child {
margin-bottom: 0;
}
@@ -27,16 +29,19 @@
border-color: #eed3d7;
color: #b94a48;
}
+
.bs-callout-warning {
background-color: #faf8f0;
border-color: #faebcc;
color: #8a6d3b;
}
+
.bs-callout-info {
background-color: #f4f8fa;
border-color: #bce8f1;
color: #34789a;
}
+
.bs-callout-success {
background-color: #dff0d8;
border-color: #5ca64d;
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 5957dce89bc..b24fce6f0c2 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -1,31 +1,31 @@
/** COLORS **/
.cgray { color: $gl-gray; }
-.clgray { color: #bbb }
+.clgray { color: #bbb; }
.cred { color: $gl-text-red; }
.cgreen { color: $gl-text-green; }
-.cdark { color: #444 }
+.cdark { color: #444; }
/** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; }
.prepend-top-5 { margin-top: 5px; }
-.prepend-top-10 { margin-top: 10px }
+.prepend-top-10 { margin-top: 10px; }
.prepend-top-default { margin-top: $gl-padding !important; }
-.prepend-top-20 { margin-top: 20px }
-.prepend-left-5 { margin-left: 5px }
-.prepend-left-10 { margin-left: 10px }
+.prepend-top-20 { margin-top: 20px; }
+.prepend-left-5 { margin-left: 5px; }
+.prepend-left-10 { margin-left: 10px; }
.prepend-left-default { margin-left: $gl-padding; }
-.prepend-left-20 { margin-left: 20px }
-.append-right-5 { margin-right: 5px }
-.append-right-10 { margin-right: 10px }
+.prepend-left-20 { margin-left: 20px; }
+.append-right-5 { margin-right: 5px; }
+.append-right-10 { margin-right: 10px; }
.append-right-default { margin-right: $gl-padding; }
-.append-right-20 { margin-right: 20px }
-.append-bottom-0 { margin-bottom: 0 }
-.append-bottom-10 { margin-bottom: 10px }
-.append-bottom-15 { margin-bottom: 15px }
-.append-bottom-20 { margin-bottom: 20px }
+.append-right-20 { margin-right: 20px; }
+.append-bottom-0 { margin-bottom: 0; }
+.append-bottom-10 { margin-bottom: 10px; }
+.append-bottom-15 { margin-bottom: 15px; }
+.append-bottom-20 { margin-bottom: 20px; }
.append-bottom-default { margin-bottom: $gl-padding; }
-.inline { display: inline-block }
-.center { text-align: center }
+.inline { display: inline-block; }
+.center { text-align: center; }
.underlined-link { text-decoration: underline; }
.hint { font-style: italic; color: #999; }
@@ -97,6 +97,7 @@ span.update-author {
color: #999;
font-weight: normal;
font-style: italic;
+
strong {
font-weight: bold;
font-style: normal;
@@ -128,7 +129,7 @@ p.time {
// Fix issue with notes & lists creating a bunch of bottom borders.
li.note {
- img { max-width: 100% }
+ img { max-width: 100%; }
.note-title {
li {
border-bottom: none !important;
@@ -142,7 +143,8 @@ li.note {
}
}
-.wiki_content code, .readme code {
+.wiki_content code,
+.readme code {
background-color: inherit;
}
@@ -172,6 +174,7 @@ li.note {
@extend .col-md-6;
text-align: left;
margin-top: 40px;
+
pre {
background: white;
border: none;
@@ -197,6 +200,7 @@ li.note {
background: #c67;
color: #fff;
font-weight: bold;
+
a {
color: #fff;
text-decoration: underline;
@@ -227,6 +231,7 @@ li.note {
&.milestone-closed {
background: $gray-light;
}
+
.progress {
margin-bottom: 0;
margin-top: 4px;
@@ -286,6 +291,7 @@ table {
.footer-links {
margin-bottom: 20px;
+
a {
margin-right: 15px;
}
@@ -293,6 +299,10 @@ table {
.well {
margin-bottom: $gl-padding;
+
+ hr {
+ border-color: $gray-darker;
+ }
}
.search_box {
@@ -345,7 +355,8 @@ table {
margin-right: 10px;
}
-.alert, .progress {
+.alert,
+.progress {
margin-bottom: $gl-padding;
}
@@ -367,3 +378,21 @@ table {
margin-right: -$gl-padding;
border-top: 1px solid $border-color;
}
+
+.hide-bottom-border { border-bottom: none !important; }
+
+.gl-accessibility {
+ &:focus {
+ top: 1px;
+ left: 1px;
+ width: auto;
+ height: 100%;
+ line-height: 50px;
+ padding: 0 10px;
+ clip: auto;
+ text-decoration: none;
+ color: $gl-title-color;
+ background: $gray-light;
+ z-index: 1;
+ }
+}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index b0ba112476b..583c17e4a83 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -1,20 +1,3 @@
-.caret {
- display: inline-block;
- width: 0;
- height: 0;
- margin-left: 2px;
- vertical-align: middle;
- border-top: $caret-width-base dashed;
- border-right: $caret-width-base solid transparent;
- border-left: $caret-width-base solid transparent;
-}
-
-.btn-group {
- .caret {
- margin-left: 0;
- }
-}
-
.dropdown {
position: relative;
@@ -29,8 +12,10 @@
.dropdown-menu,
.dropdown-menu-nav {
display: block;
+
@media (max-width: $screen-xs-max) {
width: 100%;
+ min-width: 240px;
}
}
@@ -51,26 +36,28 @@
color: $dropdown-toggle-color;
font-size: 15px;
text-align: left;
- border: 1px solid $dropdown-toggle-border-color;
+ border: 1px solid $border-color;
border-radius: $border-radius-base;
- outline: 0;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
.fa {
position: absolute;
- top: 50%;
- right: 6px;
- margin-top: -6px;
+ top: 10px;
+ right: 8px;
color: $dropdown-toggle-icon-color;
- font-size: 10px;
+
&.fa-spinner {
font-size: 16px;
margin-top: -8px;
}
}
+ &.no-outline {
+ outline: 0;
+ }
+
&:hover, {
border-color: $dropdown-toggle-hover-border-color;
@@ -290,7 +277,8 @@
a {
padding-left: 25px;
- &.is-indeterminate, &.is-active {
+ &.is-indeterminate,
+ &.is-active {
&::before {
position: absolute;
left: 5px;
@@ -388,7 +376,8 @@
}
}
-.dropdown-input-field, .default-dropdown-input {
+.dropdown-input-field,
+.default-dropdown-input {
width: 100%;
min-height: 30px;
padding: 0 7px;
@@ -417,7 +406,7 @@
.dropdown-content {
max-height: 215px;
- overflow-y: scroll;
+ overflow-y: auto;
}
.dropdown-footer {
@@ -498,7 +487,7 @@
font-size: 20px;
text-indent: 0;
- &:before {
+ &::before {
display: block;
position: relative;
top: -2px;
@@ -530,7 +519,7 @@
background-color: transparent;
border: 0;
- .ui-icon:before {
+ .ui-icon::before {
color: $md-link-color;
}
}
@@ -539,7 +528,7 @@
.ui-datepicker-prev {
left: 0;
- .ui-icon:before {
+ .ui-icon::before {
content: '\f104';
text-align: left;
}
@@ -548,7 +537,7 @@
.ui-datepicker-next {
right: 0;
- .ui-icon:before {
+ .ui-icon::before {
content: '\f105';
text-align: right;
}
@@ -604,3 +593,9 @@
display: block;
color: $gl-placeholder-color;
}
+
+.dropdown-toggle-text {
+ &.is-default {
+ color: $gl-placeholder-color;
+ }
+}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 76a3c083697..f49d7b92a00 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -57,6 +57,7 @@
margin-top: -3px;
}
}
+
.file-content {
background: #fff;
@@ -96,22 +97,27 @@
border: none;
margin: 0;
}
+
tr {
border-bottom: 1px solid #eee;
}
+
td {
&:first-child {
border-left: none;
}
+
&:last-child {
border-right: none;
}
}
+
td.blame-commit {
padding: 0 10px;
min-width: 400px;
background: $gray-light;
}
+
td.line-numbers {
float: none;
border-left: 1px solid #ddd;
@@ -121,6 +127,7 @@
margin-right: 0;
}
}
+
td.lines {
padding: 0;
}
@@ -137,8 +144,10 @@
border-left: 1px solid $border-color;
margin-bottom: 0;
background: white;
+
li {
color: #888;
+
p {
margin: 0;
color: #333;
@@ -158,7 +167,6 @@
*/
&.code {
padding: 0;
- -webkit-overflow-scrolling: auto; // See https://gitlab.com/gitlab-org/gitlab-ce/issues/13987
}
}
}
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index 0c21d0240b3..a9006de6d3e 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -3,7 +3,8 @@
margin: 0;
margin-bottom: $gl-padding;
font-size: 14px;
- z-index: 100;
+ position: relative;
+ z-index: 1;
.flash-notice {
@extend .alert;
@@ -17,10 +18,12 @@
margin: 0;
}
- .flash-notice, .flash-alert {
+ .flash-notice,
+ .flash-alert {
border-radius: $border-radius-default;
- .container-fluid.container-limited.flash-text {
+ .container-fluid,
+ .container-fluid.container-limited {
background: transparent;
}
}
@@ -28,7 +31,8 @@
&.flash-container-page {
margin-bottom: 0;
- .flash-notice, .flash-alert {
+ .flash-notice,
+ .flash-alert {
border-radius: 0;
}
}
@@ -41,4 +45,3 @@
}
}
}
-
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 37ff7e22ed1..e83a1f7ad68 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -9,7 +9,7 @@ input {
input[type='text'].danger {
background: #f2dede!important;
border-color: #d66;
- text-shadow: 0 1px 1px #fff
+ text-shadow: 0 1px 1px #fff;
}
.datetime-controls {
@@ -68,23 +68,63 @@ label {
}
}
+.help-form .form-group {
+ margin-left: 0;
+ margin-right: 0;
+
+ .control-label {
+ font-weight: bold;
+ padding-top: 4px;
+ }
+
+ .form-control {
+ height: 29px;
+ background: $white-light;
+ font-family: $monospace_font;
+ }
+
+ .input-group-btn .btn {
+ padding: 3px $gl-btn-padding;
+ background-color: $gray-light;
+ border: 1px solid $border-color;
+ }
+
+ .text-block {
+ line-height: 0.8;
+ padding-top: 9px;
+
+ code {
+ line-height: 1.8;
+ }
+ }
+
+ @media(max-width: $screen-sm-min) {
+ padding: 0 $gl-padding;
+
+ .control-label,
+ .text-block {
+ padding-left: 0;
+ }
+ }
+}
+
.fieldset-form fieldset {
margin-bottom: 20px;
}
.form-control {
@include box-shadow(none);
- border-radius: 3px;
+ border-radius: 2px;
padding: $gl-vert-padding $gl-input-padding;
}
.select-wrapper {
position: relative;
- .caret {
+ .fa-caret-down {
position: absolute;
right: 10px;
- top: $gl-padding;
+ top: 10px;
color: $gray-darkest;
pointer-events: none;
}
@@ -117,9 +157,11 @@ label {
display: table-cell;
width: 200px !important;
}
+
.input-group-addon {
background-color: #f7f8fa;
}
+
.input-group-addon:not(:first-child):not(:last-child) {
border-left: 0;
border-right: 0;
@@ -129,3 +171,39 @@ label {
.help-block {
margin-bottom: 0;
}
+
+.gl-field-error {
+ color: $red-normal;
+}
+
+.gl-show-field-errors {
+ .gl-field-success-outline {
+ border: 1px solid $green-normal;
+
+ &:focus {
+ box-shadow: 0 0 0 1px $green-normal inset, 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 4px 0 $green-normal;
+ border: 0 none;
+ }
+ }
+
+ .gl-field-error-outline {
+ border: 1px solid $red-normal;
+
+ &:focus {
+ box-shadow: 0 0 0 1px $red-normal inset, 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 4px 0 rgba(210, 40, 82, 0.6);
+ border: 0 none;
+ }
+ }
+
+ .gl-field-success-message {
+ color: $green-normal;
+ }
+
+ .gl-field-error-message {
+ color: $red-normal;
+ }
+
+ .gl-field-hint {
+ color: $gl-text-color;
+ }
+}
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 3673b81f183..91ab1503439 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -21,55 +21,66 @@
background: $color-darker;
}
- .nav-sidebar li {
- a {
- color: $color-light;
-
- &:hover, &:focus, &:active {
- background: $color-dark;
- }
+ .sidebar-header,
+ .sidebar-action-buttons {
+ color: $color-light;
+ background-color: lighten($color-darker, 5%);
+ }
- i {
+ .nav-sidebar {
+ li {
+ a {
color: $color-light;
- }
-
- path,
- polygon {
- fill: $color-light;
- }
- .count {
- color: $color-light;
- background: $color-dark;
+ &:hover,
+ &:focus,
+ &:active {
+ background: $color-dark;
+ }
+
+ i {
+ color: $color-light;
+ }
+
+ path,
+ polygon {
+ fill: $color-light;
+ }
+
+ .count {
+ color: $color-light;
+ background: $color-dark;
+ }
+
+ svg {
+ position: relative;
+ top: 3px;
+ }
}
- svg {
- position: relative;
- top: 3px;
+ &.separate-item {
+ border-top: 1px solid $color;
}
- }
- &.separate-item {
- border-top: 1px solid $color;
- }
-
- &.active a {
- color: $white-light;
- background: $color-dark;
+ &.active a {
+ color: $white-light;
+ background: $color-dark;
- &.no-highlight {
- border: none;
- }
+ &.no-highlight {
+ border: none;
+ }
- i {
- color: $white-light
- }
+ i {
+ color: $white-light;
+ }
- path,
- polygon {
- fill: $white-light;
+ path,
+ polygon {
+ fill: $white-light;
+ }
}
}
+
}
}
}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index d4a030f7f7a..16ecf466931 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -15,7 +15,8 @@ header {
margin: 8px 0;
text-align: center;
- .tanuki-logo, img {
+ .tanuki-logo,
+ img {
height: 36px;
}
}
@@ -48,14 +49,29 @@ header {
font-size: 18px;
padding: 0;
margin: ($header-height - 28) / 2 0;
- margin-left: 10px;
+ margin-left: 8px;
height: 28px;
min-width: 28px;
line-height: 28px;
text-align: center;
- &:hover, &:focus, &:active {
+ &.header-user-dropdown-toggle {
+ margin-left: 14px;
+ }
+
+ &:hover,
+ &:focus,
+ &:active {
background-color: $background-color;
+ color: darken($gl-icon-color, 30%);
+
+ .todos-pending-count {
+ background: darken($todo-alert-blue, 10%);
+ }
+ }
+
+ .fa-caret-down {
+ font-size: 15px;
}
}
@@ -89,10 +105,6 @@ header {
&:hover {
background-color: $btn-gray-hover;
}
-
- &:focus {
- outline: none;
- }
}
}
@@ -112,12 +124,17 @@ header {
.header-logo {
position: absolute;
left: 50%;
- margin-left: -18px;
top: 7px;
transition-duration: .3s;
z-index: 999;
- svg, img {
+ #logo {
+ position: relative;
+ left: -50%;
+ }
+
+ svg,
+ img {
height: 36px;
}
@@ -126,8 +143,12 @@ header {
}
@media (max-width: $screen-xs-max) {
- right: 25px;
+ right: 20px;
left: auto;
+
+ #logo {
+ left: auto;
+ }
}
}
@@ -136,7 +157,7 @@ header {
padding-right: 20px;
margin: 0;
font-size: 19px;
- max-width: 400px;
+ max-width: 385px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
@@ -156,6 +177,7 @@ header {
a {
color: $gl-text-color;
+
&:hover {
text-decoration: underline;
}
@@ -174,6 +196,10 @@ header {
font-size: 10px;
text-align: center;
cursor: pointer;
+
+ &:hover {
+ color: darken($color: $gl-text-color, $amount: 30%);
+ }
}
.project-item-select {
@@ -201,6 +227,14 @@ header {
}
}
+.page-sidebar-pinned.right-sidebar-expanded {
+ @media (max-width: $screen-lg-min) {
+ .header-content .title {
+ width: 300px;
+ }
+ }
+}
+
@media (max-width: $screen-xs-max) {
header .container-fluid {
font-size: 18px;
@@ -209,7 +243,8 @@ header {
margin: 0;
float: none !important;
- .visible-xs, .visable-sm {
+ .visible-xs,
+ .visible-sm {
display: table-cell !important;
}
}
diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss
index 07c8874bf03..909a0f4afda 100644
--- a/app/assets/stylesheets/framework/highlight.scss
+++ b/app/assets/stylesheets/framework/highlight.scss
@@ -11,7 +11,7 @@
border-radius: 0;
font-family: $monospace_font;
font-size: $code_font_size;
- line-height: $code_line_height !important;
+ line-height: 19px;
margin: 0;
overflow: auto;
overflow-y: hidden;
@@ -47,7 +47,7 @@
font-family: $monospace_font;
display: block;
font-size: $code_font_size !important;
- line-height: $code_line_height !important;
+ line-height: 19px;
white-space: nowrap;
i {
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 8bfc0d583c5..ff6f316d576 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -16,7 +16,7 @@
margin-top: 5px;
}
- @include border-radius(3px);
+ border-radius: 3px;
display: block;
float: left;
margin-right: 10px;
@@ -39,4 +39,8 @@
&.status-box-expired {
background: #cea61b;
}
+
+ &.status-box-upcoming {
+ background: #8f8f8f;
+ }
}
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index 8bb047db2dd..7baa4296abf 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -27,3 +27,15 @@ body {
.container-limited {
max-width: $fixed-layout-width;
}
+
+
+/* The following prevents side effects related to iOS Safari's implementation of -webkit-overflow-scrolling: touch,
+which is applied to the body by jquery.nicescroling plugin to force hardware acceleration for momentum scrolling. Side
+effects are commonly related to inconsisent z-index behavior (e.g. tooltips). By applying the following to direct children
+of the body element here, we negate cascading side effects but allow momentum scrolling to be applied to the body */
+
+.navbar,
+.page-gutter,
+.page-with-sidebar {
+ -webkit-overflow-scrolling: auto;
+}
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 46af18580d5..bc0610cc417 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -14,7 +14,7 @@
border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
- &:after {
+ &::after {
content: " ";
display: table;
clear: both;
@@ -38,7 +38,7 @@
&.smoke { background-color: $background-color; }
- &:hover {
+ &:not(.ui-sort-disabled):hover {
background: $row-hover;
}
@@ -60,6 +60,7 @@
padding-top: 1px;
margin: 0;
color: $gray-dark;
+
img {
position: relative;
top: 3px;
@@ -75,14 +76,16 @@
/** light list with border-bottom between li **/
-ul.bordered-list, ul.unstyled-list {
+ul.bordered-list,
+ul.unstyled-list {
@include basic-list;
&.top-list {
li:first-child {
padding-top: 0;
- h4, h5 {
+ h4,
+ h5 {
margin-top: 0;
}
}
@@ -128,6 +131,10 @@ ul.content-list {
color: $gl-dark-link-color;
}
+ .member-group-link {
+ color: $blue-normal;
+ }
+
.description {
p {
@include str-truncated;
@@ -135,10 +142,6 @@ ul.content-list {
}
}
- .avatar {
- margin-right: 15px;
- }
-
.controls {
float: right;
@@ -164,7 +167,15 @@ ul.content-list {
}
.no-comments {
- opacity: 0.5;
+ opacity: .5;
+ }
+ }
+
+ .member-controls {
+ float: none;
+
+ @media (min-width: $screen-sm-min) {
+ float: right;
}
}
diff --git a/app/assets/stylesheets/framework/logo.scss b/app/assets/stylesheets/framework/logo.scss
index 3ee3fb4cee5..429cfbe7235 100644
--- a/app/assets/stylesheets/framework/logo.scss
+++ b/app/assets/stylesheets/framework/logo.scss
@@ -1,15 +1,3 @@
-@mixin unique-keyframes {
- $animation-name: unique-id();
- @include webkit-prefix(animation-name, $animation-name);
-
- @-webkit-keyframes #{$animation-name} {
- @content;
- }
- @keyframes #{$animation-name} {
- @content;
- }
-}
-
@mixin tanuki-logo-colors($path-color) {
fill: $path-color;
transition: all 0.8s;
@@ -20,28 +8,6 @@
}
}
-@mixin tanuki-second-highlight-animations($tanuki-color) {
- @include unique-keyframes {
- 10%, 80% {
- fill: #{$tanuki-color}
- }
- 20%, 90% {
- fill: lighten($tanuki-color, 25%);
- }
- }
-}
-
-@mixin tanuki-forth-highlight-animations($tanuki-color) {
- @include unique-keyframes {
- 30%, 60% {
- fill: #{$tanuki-color};
- }
- 40%, 70% {
- fill: lighten($tanuki-color, 25%);
- }
- }
-}
-
.tanuki-logo {
.tanuki-left-ear,
@@ -67,10 +33,11 @@
}
.tanuki-left-cheek {
- @include unique-keyframes {
+ @include include-keyframes(animate-tanuki-left-cheek) {
0%, 10%, 100% {
fill: lighten($tanuki-yellow, 25%);
}
+
90% {
fill: $tanuki-yellow;
}
@@ -78,18 +45,35 @@
}
.tanuki-left-eye {
- @include tanuki-second-highlight-animations($tanuki-orange);
+ @include include-keyframes(animate-tanuki-left-eye) {
+ 10%, 80% {
+ fill: $tanuki-orange;
+ }
+
+ 20%, 90% {
+ fill: lighten($tanuki-orange, 25%);
+ }
+ }
}
.tanuki-left-ear {
- @include tanuki-second-highlight-animations($tanuki-red);
+ @include include-keyframes(animate-tanuki-left-ear) {
+ 10%, 80% {
+ fill: $tanuki-red;
+ }
+
+ 20%, 90% {
+ fill: lighten($tanuki-red, 25%);
+ }
+ }
}
.tanuki-nose {
- @include unique-keyframes {
+ @include include-keyframes(animate-tanuki-nose) {
20%, 70% {
fill: $tanuki-red;
}
+
30%, 80% {
fill: lighten($tanuki-red, 25%);
}
@@ -97,22 +81,39 @@
}
.tanuki-right-eye {
- @include tanuki-forth-highlight-animations($tanuki-orange);
+ @include include-keyframes(animate-tanuki-right-eye) {
+ 30%, 60% {
+ fill: $tanuki-orange;
+ }
+
+ 40%, 70% {
+ fill: lighten($tanuki-orange, 25%);
+ }
+ }
}
.tanuki-right-ear {
- @include tanuki-forth-highlight-animations($tanuki-red);
+ @include include-keyframes(animate-tanuki-right-ear) {
+ 30%, 60% {
+ fill: $tanuki-red;
+ }
+
+ 40%, 70% {
+ fill: lighten($tanuki-red, 25%);
+ }
+ }
}
.tanuki-right-cheek {
- @include unique-keyframes {
+ @include include-keyframes(animate-tanuki-right-cheek) {
40% {
fill: $tanuki-yellow;
}
+
60% {
fill: lighten($tanuki-yellow, 25%);
}
}
}
}
-} \ No newline at end of file
+}
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index edea4ad00eb..42087c91530 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -64,12 +64,17 @@
a {
padding-top: 0;
- line-height: 1;
+ line-height: 19px;
border-bottom: 1px solid $border-color;
&.btn.btn-xs {
padding: 2px 5px;
}
+
+ &:focus {
+ margin-top: -10px;
+ padding-top: 10px;
+ }
}
}
}
@@ -86,7 +91,7 @@
}
.markdown-area {
- @include border-radius(0);
+ border-radius: 0;
background: #fff;
border: 1px solid #ddd;
min-height: 140px;
@@ -148,7 +153,19 @@
}
}
-.atwho-view small.description {
- float: right;
- padding: 3px 5px;
+.atwho-view {
+ small.description {
+ float: right;
+ padding: 3px 5px;
+ }
+
+ .avatar-inline {
+ margin-bottom: 0;
+ }
+
+ .cur {
+ .avatar {
+ border: 1px solid $white-light;
+ }
+ }
}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 1ec08cdef23..f84ca36d10f 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -1,15 +1,4 @@
/**
- * Generic mixins
- */
-@mixin box-shadow($shadow) {
- box-shadow: $shadow;
-}
-
-@mixin border-radius($radius) {
- border-radius: $radius;
-}
-
-/**
* Prefilled mixins
* Mixins with fixed values
*/
@@ -45,6 +34,7 @@
&.active {
background: $gray-light;
+
a {
font-weight: 600;
}
@@ -95,3 +85,10 @@
@content;
}
}
+
+@mixin include-keyframes($animation-name) {
+ @include webkit-prefix(animation-name, $animation-name);
+ @include keyframes($animation-name) {
+ @content;
+ }
+}
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index 76b93b23b95..9391661a595 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -71,7 +71,7 @@
display: none;
}
- .group-right-buttons {
+ .group-buttons {
display: none;
}
@@ -79,7 +79,8 @@
padding-left: 15px !important;
}
- .nav-links, .nav-links {
+ .nav-links,
+ .nav-links {
li a {
font-size: 14px;
padding: 19px 10px;
@@ -99,18 +100,21 @@
@media (max-width: $screen-sm-max) {
.issues-filters {
- .milestone-filter, .labels-filter {
+ .milestone-filter,
+ .labels-filter {
display: none;
}
}
.page-title {
- .note-created-ago, .new-issue-link {
+ .note-created-ago,
+ .new-issue-link {
display: none;
}
}
- .issue_edited_ago, .note_edited_ago {
+ .issue_edited_ago,
+ .note_edited_ago {
display: none;
}
@@ -133,5 +137,5 @@
font-size: 20px;
color: #777;
z-index: 100;
- @include box-shadow(0 1px 2px #ddd);
+ box-shadow: 0 1px 2px #ddd;
}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index 8374f30d0b2..8cd49280e1c 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -3,7 +3,7 @@
padding: 15px;
.form-actions {
- margin: -$gl-padding+1;
+ margin: -$gl-padding + 1;
margin-top: 15px;
}
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 553768b2e68..ce864c2de5e 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -54,9 +54,10 @@
color: #959494;
border-bottom: 2px solid transparent;
- &:hover, &:active, &:focus {
+ &:hover,
+ &:active,
+ &:focus {
text-decoration: none;
- outline: none;
}
}
@@ -99,8 +100,7 @@
.top-area {
@include clearfix;
-
- border-bottom: 1px solid #eee;
+ border-bottom: 1px solid $btn-gray-hover;
.nav-text {
padding-top: 16px;
@@ -211,7 +211,12 @@
@media (max-width: $screen-xs-max) {
padding-bottom: 0;
width: 100%;
- .btn, form, .dropdown, .dropdown-menu-toggle, .form-control {
+
+ .btn,
+ form,
+ .dropdown,
+ .dropdown-menu-toggle,
+ .form-control {
margin: 0 0 10px;
display: block;
width: 100%;
@@ -245,7 +250,8 @@
}
&.adjust {
- .nav-text, .nav-controls {
+ .nav-text,
+ .nav-controls {
width: auto;
}
}
@@ -309,13 +315,15 @@
padding-top: 10px;
}
- a, i {
+ a,
+ i {
color: $layout-link-gray;
}
&.active {
- a, i {
+ a,
+ i {
color: $black;
}
@@ -328,7 +336,8 @@
}
&:hover {
- a, i {
+ a,
+ i {
color: $black;
}
}
diff --git a/app/assets/stylesheets/framework/page-header.scss b/app/assets/stylesheets/framework/page-header.scss
new file mode 100644
index 00000000000..85c1385d5d9
--- /dev/null
+++ b/app/assets/stylesheets/framework/page-header.scss
@@ -0,0 +1,67 @@
+.page-content-header {
+ line-height: 34px;
+ padding: 10px 0;
+ margin-bottom: 0;
+
+ @media (min-width: $screen-sm-min) {
+ display: flex;
+ align-items: center;
+
+ .header-main-content {
+ flex: 1;
+ }
+ }
+
+ .header-action-buttons {
+ i {
+ color: $gl-icon-color;
+ font-size: 13px;
+ margin-right: 3px;
+ }
+
+ @media (max-width: $screen-xs-max) {
+ .btn {
+ width: 100%;
+ margin-top: 10px;
+ }
+
+ .dropdown {
+ width: 100%;
+ }
+ }
+ }
+
+ .avatar {
+ @extend .avatar-inline;
+ margin-left: 0;
+
+ @media (min-width: $screen-sm-min) {
+ margin-left: 4px;
+ }
+ }
+
+ .commit-committer-link,
+ .commit-author-link {
+ color: $gl-gray;
+ font-weight: bold;
+ }
+
+ .fa-clipboard {
+ color: $dropdown-title-btn-color;
+ }
+
+ .commit-info {
+ &.branches {
+ margin-left: 8px;
+ }
+ }
+
+ .ci-status-link {
+
+ svg {
+ position: relative;
+ top: 2px;
+ margin: 0 2px 0 3px;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index b6f21fd8c91..cb2c351c368 100644
--- a/app/assets/stylesheets/framework/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
@@ -7,8 +7,70 @@
.pagination {
padding: 0;
}
+
+ .gap,
+ .gap:hover {
+ background-color: $gray-light;
+ padding: $gl-vert-padding;
+ cursor: default;
+ }
}
.panel > .gl-pagination {
margin: 0;
}
+
+/**
+ * Extra-small screen pagination.
+ */
+@media (max-width: 320px) {
+ .gl-pagination {
+ .first,
+ .last {
+ display: none;
+ }
+
+ .page {
+ display: none;
+
+ &.active {
+ display: inline;
+ }
+ }
+ }
+}
+
+/**
+ * Small screen pagination
+ */
+@media (max-width: $screen-xs) {
+ .gl-pagination {
+ .pagination li a {
+ padding: 6px 10px;
+ }
+
+ .page {
+ display: none;
+
+ &.active {
+ display: inline;
+ }
+ }
+ }
+}
+
+/**
+ * Medium screen pagination
+ */
+@media (min-width: $screen-xs) and (max-width: $screen-md-max) {
+ .gl-pagination {
+ .page {
+ display: none;
+
+ &.active,
+ &.sibling {
+ display: inline;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index c6f30e144fd..5ba0486177f 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -13,6 +13,11 @@
.dropdown-menu-toggle {
line-height: 20px;
}
+
+ .badge {
+ margin-top: -2px;
+ margin-left: 5px;
+ }
}
.panel-body {
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index c75dacf95d9..920ce249b9a 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -3,7 +3,8 @@
width: 100% !important;
}
-.select2-container, .select2-container.select2-drop-above {
+.select2-container,
+.select2-container.select2-drop-above {
.select2-choice {
background: #fff;
border-color: $input-border;
@@ -21,7 +22,14 @@
padding-right: 10px;
b {
- @extend .caret;
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: 2px;
+ vertical-align: middle;
+ border-top: 5px dashed;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
color: $gray-darkest;
}
}
@@ -39,8 +47,8 @@
}
.select2-drop {
- @include box-shadow(rgba(76, 86, 103, 0.247059) 0 0 1px 0, rgba(31, 37, 50, 0.317647) 0 2px 18px 0);
- @include border-radius ($border-radius-default);
+ box-shadow: rgba(76, 86, 103, 0.247059) 0 0 1px 0, rgba(31, 37, 50, 0.317647) 0 2px 18px 0;
+ border-radius: $border-radius-default;
border: none;
min-width: 175px;
}
@@ -55,7 +63,7 @@
}
.select2-highlighted {
- background: #3084bb !important;
+ background: $gl-link-color !important;
}
.select2-results li.select2-result-with-children > .select2-result-label {
@@ -64,8 +72,9 @@
}
.select2-container-active {
- .select2-choice, .select2-choices {
- @include box-shadow(none);
+ .select2-choice,
+ .select2-choices {
+ box-shadow: none;
}
}
@@ -75,18 +84,18 @@
outline: 0;
background-image: none;
background-color: $white-dark;
- @include box-shadow($gl-btn-active-gradient);
+ box-shadow: $gl-btn-active-gradient;
}
}
.select2-container-multi {
.select2-choices {
- @include border-radius($border-radius-default);
+ border-radius: $border-radius-default;
border-color: $input-border;
background: none;
.select2-search-field input {
- padding: $gl-padding / 2;
+ padding: 5px $gl-padding / 2;
font-size: 13px;
height: auto;
font-family: inherit;
@@ -94,7 +103,7 @@
}
.select2-search-choice {
- margin: 8px 0 0 8px;
+ margin: 5px 0 0 8px;
box-shadow: none;
border-color: $input-border;
color: $gl-text-color;
@@ -116,7 +125,7 @@
&.select2-container-active .select2-choices,
&.select2-dropdown-open .select2-choices {
border-color: $border-white-normal;
- @include box-shadow($gl-btn-active-gradient);
+ box-shadow: $gl-btn-active-gradient;
}
}
@@ -130,6 +139,7 @@
.select2-results {
max-height: 350px;
+
.select2-highlighted {
background: $gl-primary;
}
@@ -150,7 +160,7 @@
background-repeat: no-repeat;
background-position: right 0 bottom 6px;
border: 1px solid $input-border;
- @include border-radius($border-radius-default);
+ border-radius: $border-radius-default;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
&:focus {
@@ -205,9 +215,11 @@
.group-image {
float: left;
}
+
.group-name {
font-weight: bold;
}
+
.group-path {
color: #999;
}
@@ -232,6 +244,7 @@
color: #aaa;
font-weight: normal;
}
+
.namespace-path {
margin-left: 10px;
font-weight: bolder;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 3b7de4b57bb..44c445c0543 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -4,7 +4,7 @@
&.page-sidebar-pinned {
.sidebar-wrapper {
- @include box-shadow(none);
+ box-shadow: none;
}
}
@@ -17,7 +17,7 @@
width: 0;
overflow: hidden;
transition: width $sidebar-transition-duration;
- @include box-shadow(2px 0 16px 0 $black-transparent);
+ box-shadow: 2px 0 16px 0 $black-transparent;
}
}
@@ -59,6 +59,11 @@
padding: 0 !important;
}
+ .sidebar-header {
+ padding: 11px 22px 12px;
+ font-size: 20px;
+ }
+
li {
&.separate-item {
padding-top: 10px;
@@ -78,7 +83,6 @@
display: block;
text-decoration: none;
font-weight: normal;
- outline: none;
&:hover,
&:active,
@@ -100,7 +104,7 @@
.count {
float: right;
padding: 0 8px;
- @include border-radius(6px);
+ border-radius: 6px;
}
}
@@ -142,6 +146,7 @@
transition-duration: .3s;
position: absolute;
top: 0;
+ cursor: pointer;
&:hover,
&:focus {
@@ -163,6 +168,18 @@
padding-left: $sidebar_width;
}
}
+
+ .merge-request-tabs-holder.affix {
+ @media (min-width: $sidebar-breakpoint) {
+ left: $sidebar_width;
+ }
+ }
+
+ &.right-sidebar-expanded {
+ .line-resolve-all-container {
+ display: none;
+ }
+ }
}
header.header-sidebar-pinned {
@@ -184,6 +201,10 @@ header.header-sidebar-pinned {
@media (min-width: $screen-sm-min) {
padding-right: $sidebar_collapsed_width;
+
+ .merge-request-tabs-holder.affix {
+ right: $sidebar_collapsed_width;
+ }
}
.sidebar-collapsed-icon {
@@ -206,6 +227,10 @@ header.header-sidebar-pinned {
@media (min-width: $screen-md-min) {
padding-right: $gutter_width;
+
+ .merge-request-tabs-holder.affix {
+ right: $gutter_width;
+ }
}
&.with-overlay {
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index b42075c98d0..9a90d3794fd 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -23,7 +23,8 @@ table {
}
tr {
- td, th {
+ td,
+ th {
padding: 10px $gl-padding;
line-height: 20px;
vertical-align: middle;
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 0b0bd80c326..875cded8b4e 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -45,9 +45,10 @@
@media (max-width: $screen-xs-max) {
.timeline {
- &:before {
+ &::before {
background: none;
}
+
.timeline-entry .timeline-entry-inner {
.timeline-icon {
display: none;
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index e3154657c54..59f4594bb83 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -48,31 +48,40 @@
.clearfix {
@include clearfix();
}
+
.center-block {
@include center-block();
}
+
.pull-right {
float: right !important;
}
+
.pull-left {
float: left !important;
}
+
.hide {
display: none;
}
+
.show {
display: block !important;
}
+
.invisible {
visibility: hidden;
}
+
.text-hide {
@include text-hide();
}
+
.hidden {
display: none !important;
visibility: hidden !important;
}
+
.affix {
position: fixed;
}
@@ -117,7 +126,8 @@
box-shadow: none;
.panel-body {
- form, pre {
+ form,
+ pre {
margin: 0;
}
@@ -146,6 +156,7 @@
padding: 6px 15px;
font-size: 13px;
font-weight: normal;
+
a {
color: #777;
}
diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index 915aa631ef8..44fe37d3a4a 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -16,21 +16,21 @@
// $gray-light: lighten($gray-base, 46.7%) // #777
// $gray-lighter: lighten($gray-base, 93.5%) // #eee
-$brand-primary: $gl-primary;
-$brand-success: $gl-success;
-$brand-info: $gl-info;
-$brand-warning: $gl-warning;
-$brand-danger: $gl-danger;
+$brand-primary: $gl-primary;
+$brand-success: $gl-success;
+$brand-info: $gl-info;
+$brand-warning: $gl-warning;
+$brand-danger: $gl-danger;
-$border-radius-base: 3px !default;
-$border-radius-large: 3px !default;
-$border-radius-small: 3px !default;
+$border-radius-base: 3px !default;
+$border-radius-large: 3px !default;
+$border-radius-small: 3px !default;
//== Scaffolding
//
-$text-color: $gl-text-color;
-$link-color: $gl-link-color;
+$text-color: $gl-text-color;
+$link-color: $gl-link-color;
//== Typography
@@ -38,112 +38,112 @@ $link-color: $gl-link-color;
//## Font, line-height, and color for body text, headings, and more.
$font-family-sans-serif: $regular_font;
-$font-family-monospace: $monospace_font;
-$font-size-base: $gl-font-size;
+$font-family-monospace: $monospace_font;
+$font-size-base: $gl-font-size;
//== Components
//
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
-$padding-base-vertical: $gl-vert-padding;
-$padding-base-horizontal: $gl-padding;
-$component-active-color: #fff;
-$component-active-bg: $brand-info;
+$padding-base-vertical: $gl-vert-padding;
+$padding-base-horizontal: $gl-padding;
+$component-active-color: #fff;
+$component-active-bg: $brand-info;
//== Forms
//
//##
-$input-color: $text-color;
-$input-border: $border-color;
-$input-border-focus: $focus-border-color;
-$legend-color: $text-color;
+$input-color: $text-color;
+$input-border: $border-color;
+$input-border-focus: $focus-border-color;
+$legend-color: $text-color;
//== Pagination
//
//##
-$pagination-color: $gl-gray;
-$pagination-bg: #fff;
-$pagination-border: $border-color;
+$pagination-color: $gl-gray;
+$pagination-bg: #fff;
+$pagination-border: $border-color;
-$pagination-hover-color: $gl-gray;
-$pagination-hover-bg: $row-hover;
-$pagination-hover-border: $border-color;
+$pagination-hover-color: $gl-gray;
+$pagination-hover-bg: $row-hover;
+$pagination-hover-border: $border-color;
-$pagination-active-color: $blue-dark;
-$pagination-active-bg: #fff;
-$pagination-active-border: $border-color;
+$pagination-active-color: $blue-dark;
+$pagination-active-bg: #fff;
+$pagination-active-border: $border-color;
-$pagination-disabled-color: #cdcdcd;
-$pagination-disabled-bg: $background-color;
-$pagination-disabled-border: $border-color;
+$pagination-disabled-color: #cdcdcd;
+$pagination-disabled-bg: $background-color;
+$pagination-disabled-border: $border-color;
//== Form states and alerts
//
//## Define colors for form feedback states and, by default, alerts.
-$state-success-text: #fff;
-$state-success-bg: $brand-success;
-$state-success-border: $brand-success;
+$state-success-text: #fff;
+$state-success-bg: $brand-success;
+$state-success-border: $brand-success;
-$state-info-text: #fff;
-$state-info-bg: $brand-info;
-$state-info-border: $brand-info;
+$state-info-text: #fff;
+$state-info-bg: $brand-info;
+$state-info-border: $brand-info;
-$state-warning-text: #fff;
-$state-warning-bg: $brand-warning;
-$state-warning-border: $brand-warning;
+$state-warning-text: #fff;
+$state-warning-bg: $brand-warning;
+$state-warning-border: $brand-warning;
-$state-danger-text: #fff;
-$state-danger-bg: $brand-danger;
-$state-danger-border: $brand-danger;
+$state-danger-text: #fff;
+$state-danger-bg: $brand-danger;
+$state-danger-border: $brand-danger;
//== Alerts
//
//## Define alert colors, border radius, and padding.
-$alert-border-radius: 0;
+$alert-border-radius: 0;
//== Panels
//
//##
-$panel-border-radius: 2px;
-$panel-default-text: $text-color;
-$panel-default-border: $border-color;
+$panel-border-radius: 2px;
+$panel-default-text: $text-color;
+$panel-default-border: $border-color;
$panel-default-heading-bg: $background-color;
-$panel-footer-bg: $background-color;
-$panel-inner-border: $border-color;
+$panel-footer-bg: $background-color;
+$panel-inner-border: $border-color;
//== Wells
//
//##
-$well-bg: $gray-light;
-$well-border: #eee;
+$well-bg: $gray-light;
+$well-border: #eee;
//== Code
//
//##
-$code-color: #c7254e;
-$code-bg: #f9f2f4;
+$code-color: #c7254e;
+$code-bg: #f9f2f4;
-$kbd-color: #fff;
-$kbd-bg: #333;
+$kbd-color: #fff;
+$kbd-bg: #333;
//== Buttons
//
//##
-$btn-default-color: $gl-text-color;
-$btn-default-bg: #fff;
-$btn-default-border: #e7e9ed;
+$btn-default-color: $gl-text-color;
+$btn-default-bg: #fff;
+$btn-default-border: #e7e9ed;
//== Nav
//
@@ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding;
//== Code
//
//##
-$pre-bg: $background-color !default;
-$pre-color: $gl-gray !default;
+$pre-bg: $background-color !default;
+$pre-color: $gl-gray !default;
$pre-border-color: $border-color;
$table-bg-accent: $background-color;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 3f8433a0e7f..070e42d63d2 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -45,40 +45,38 @@
}
h1 {
- font-size: 2em;
+ font-size: 1.75em;
font-weight: 600;
- margin: 1em 0 10px;
+ margin: 16px 0 10px;
padding: 0 0 0.3em;
- border-bottom: 1px solid $btn-default-border;
+ border-bottom: 1px solid $white-dark;
color: $gl-gray-dark;
}
h2 {
- font-size: 1.6em;
+ font-size: 1.5em;
font-weight: 600;
- margin: 1em 0 10px;
- padding-bottom: 0.3em;
- border-bottom: 1px solid $btn-default-border;
+ margin: 16px 0 10px;
color: $gl-gray-dark;
}
h3 {
- margin: 1em 0 10px;
- font-size: 1.4em;
+ margin: 16px 0 10px;
+ font-size: 1.3em;
}
h4 {
- margin: 1em 0 10px;
- font-size: 1.25em;
+ margin: 16px 0 10px;
+ font-size: 1.2em;
}
h5 {
- margin: 1em 0 10px;
+ margin: 16px 0 10px;
font-size: 1em;
}
h6 {
- margin: 1em 0 10px;
+ margin: 16px 0 10px;
font-size: 0.95em;
}
@@ -87,7 +85,12 @@
font-size: inherit;
padding: 8px 21px;
margin: 12px 0;
- border-left: 3px solid #e7e9ed;
+ border-left: 3px solid $white-dark;
+ }
+
+ blockquote:dir(rtl) {
+ border-left: 0;
+ border-right: 3px solid $white-dark;
}
blockquote p {
@@ -106,34 +109,46 @@
@extend .table-bordered;
margin: 12px 0;
color: #5c5d5e;
+
th {
background: #f8fafc;
}
}
+ table:dir(rtl) th {
+ text-align: right;
+ }
+
pre {
margin: 12px 0;
font-size: 13px;
line-height: 1.6em;
overflow-x: auto;
- @include border-radius(2px);
+ border-radius: 2px;
}
p > code {
font-weight: inherit;
}
- ul, ol {
+ ul,
+ ol {
padding: 0;
margin: 3px 0 3px 28px !important;
}
+ ul:dir(rtl),
+ ol:dir(rtl) {
+ margin: 3px 28px 3px 0 !important;
+ }
+
li {
line-height: 1.6em;
}
- a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] {
- &:before {
+ a[href*="/uploads/"],
+ a[href*="storage.googleapis.com/google-code-attachments/"] {
+ &::before {
margin-right: 4px;
font: normal normal normal 14px/1 FontAwesome;
@@ -143,19 +158,24 @@
content: "\f0c6";
}
- &:hover:before {
+ &:hover::before {
text-decoration: none;
}
}
a.no-attachment-icon {
- &:before {
+ &::before {
display: none;
}
}
/* Link to current header. */
- h1, h2, h3, h4, h5, h6 {
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
position: relative;
a.anchor {
@@ -163,13 +183,13 @@
position: absolute;
text-decoration: none;
- &:after {
- content: url('icon_anchor.svg');
+ &::after {
+ content: image-url('icon_anchor.svg');
visibility: hidden;
}
}
- &:hover > a.anchor:after {
+ &:hover > a.anchor::after {
visibility: visible;
}
}
@@ -203,8 +223,13 @@ body {
margin: 12px 7px;
}
-h1, h2, h3, h4, h5, h6 {
- color: $gl-header-color;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ color: $gl-title-color;
font-weight: 600;
}
@@ -261,7 +286,10 @@ a > code {
text-decoration: line-through;
}
-h1, h2, h3, h4 {
+h1,
+h2,
+h3,
+h4 {
small {
color: $gl-gray;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 9f563a4de35..750d99ebabe 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -16,9 +16,12 @@ $white-light: #fff;
$white-normal: #ededed;
$white-dark: #ececec;
+$gray-lightest: #fdfdfd;
$gray-light: #fafafa;
+$gray-lighter: #f9f9f9;
$gray-normal: #f5f5f5;
$gray-dark: #ededed;
+$gray-darker: #eee;
$gray-darkest: #c9c9c9;
$green-light: #38ae67;
@@ -33,6 +36,8 @@ $blue-medium-light: #3498cb;
$blue-medium: #2f8ebf;
$blue-medium-dark: #2d86b4;
+$blue-light-transparent: rgba(44, 159, 216, 0.05);
+
$orange-light: #fc8a51;
$orange-normal: #e75e40;
$orange-dark: #ce5237;
@@ -52,6 +57,7 @@ $border-gray-light: #dcdcdc;
$border-gray-normal: #d7d7d7;
$border-gray-dark: #c6cacf;
+$border-green-extra-light: #9adb84;
$border-green-light: #2faa60;
$border-green-normal: #2ca05b;
$border-green-dark: #279654;
@@ -78,38 +84,41 @@ $warning-message-border: #f0e2bb;
/*
* UI elements
*/
-$border-color: #e5e5e5;
-$focus-border-color: #3aabf0;
-$table-border-color: #f0f0f0;
-$background-color: $gray-light;
+$border-color: #e5e5e5;
+$focus-border-color: #3aabf0;
+$table-border-color: #f0f0f0;
+$background-color: $gray-light;
$dark-background-color: #f5f5f5;
-$table-text-gray: #8f8f8f;
+$table-text-gray: #8f8f8f;
+$well-expand-item: #e8f2f7;
+$well-inner-border: #eef0f2;
/*
* Text
*/
-$gl-font-size: 15px;
-$gl-title-color: #333;
-$gl-text-color: #5c5c5c;
-$gl-text-green: #4a2;
-$gl-text-red: #d12f19;
-$gl-text-orange: #d90;
-$gl-link-color: #3084bb;
-$gl-dark-link-color: #333;
+$gl-font-size: 15px;
+$gl-title-color: #333;
+$gl-text-color: #5c5c5c;
+$gl-text-color-light: #8c8c8c;
+$gl-text-green: #4a2;
+$gl-text-red: #d12f19;
+$gl-text-orange: #d90;
+$gl-link-color: #3777b0;
+$gl-dark-link-color: #333;
$gl-placeholder-color: #8f8f8f;
-$gl-icon-color: $gl-placeholder-color;
-$gl-grayish-blue: #7f8fa4;
-$gl-gray: $gl-text-color;
-$gl-gray-dark: #313236;
-$gl-gray-light: $gl-placeholder-color;
-$gl-header-color: $gl-title-color;
+$gl-icon-color: $gl-placeholder-color;
+$gl-grayish-blue: #7f8fa4;
+$gl-gray: $gl-text-color;
+$gl-gray-dark: #313236;
+$gl-gray-light: $gl-placeholder-color;
+$gl-header-color: #4c4e54;
/*
* Lists
*/
-$list-font-size: $gl-font-size;
+$list-font-size: $gl-font-size;
$list-title-color: $gl-title-color;
-$list-text-color: $gl-text-color;
+$list-text-color: $gl-text-color;
$list-text-height: 42px;
/*
@@ -151,6 +160,7 @@ $settings-icon-size: 18px;
$provider-btn-group-border: #e5e5e5;
$provider-btn-not-active-color: #4688f1;
$link-underline-blue: #4a8bee;
+$active-item-blue: #4a8bee;
$layout-link-gray: #7e7c7c;
$todo-alert-blue: #428bca;
$btn-side-margin: 10px;
@@ -188,7 +198,7 @@ $line-number-new: #ddfbe6;
$line-number-select: #fbf2da;
$match-line: $gray-light;
$table-border-gray: #f0f0f0;
-$line-target-blue: #eaf3fc;
+$line-target-blue: #f6faff;
$line-select-yellow: #fcf8e7;
$line-select-yellow-dark: #f0e2bd;
@@ -243,7 +253,7 @@ $award-emoji-new-btn-icon-color: #dcdcdc;
*/
$search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
-$search-input-width: 244px;
+$search-input-width: 220px;
$location-badge-color: #aaa;
$location-badge-bg: $gray-normal;
$location-badge-active-bg: #4f91f8;
@@ -270,6 +280,15 @@ $calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: $gray-light;
/*
+ * Cycle Analytics
+ */
+$cycle-analytics-box-padding: 30px;
+$cycle-analytics-box-text-color: #8c8c8c;
+$cycle-analytics-big-font: 19px;
+$cycle-analytics-dark-text: $gl-title-color;
+$cycle-analytics-light-gray: #bfbfbf;
+
+/*
* Personal Access Tokens
*/
$personal-access-tokens-disabled-label-color: #bbb;
diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss
new file mode 100644
index 00000000000..192939f4527
--- /dev/null
+++ b/app/assets/stylesheets/framework/wells.scss
@@ -0,0 +1,45 @@
+.info-well {
+ background: $background-color;
+ color: $gl-gray;
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+
+ .well-segment {
+ padding: $gl-padding;
+
+ &:not(:last-of-type) {
+ border-bottom: 1px solid $well-inner-border;
+ }
+
+ &.branch-info {
+ .monospace,
+ .commit-info {
+ margin-left: 4px;
+ }
+ }
+ }
+
+ .icon-container {
+ display: inline-block;
+ margin-right: 8px;
+
+ svg {
+ position: relative;
+ top: 2px;
+ height: 16px;
+ width: 16px;
+ }
+
+ &.commit-icon {
+ svg {
+ path {
+ fill: $gl-text-color;
+ }
+ }
+ }
+ }
+
+ .label.label-gray {
+ background-color: $well-expand-item;
+ }
+}
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index 16ffbe57a99..d22d9b01495 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -1,20 +1,25 @@
/* https://github.com/MozMorris/tomorrow-pygments */
.code.dark {
// Line numbers
- .line-numbers, .diff-line-num {
+ .line-numbers,
+ .diff-line-num {
background-color: #1d1f21;
}
- .diff-line-num, .diff-line-num a {
+ .diff-line-num,
+ .diff-line-num a {
color: rgba(255, 255, 255, 0.3);
}
// Code itself
- pre.code, .diff-line-num {
+ pre.code,
+ .diff-line-num {
border-color: #666;
}
- &, pre.code, .line_holder .line_content {
+ &,
+ pre.code,
+ .line_holder .line_content {
background-color: #1d1f21;
color: #c5c8c6;
}
@@ -31,11 +36,13 @@
border-color: darken(#557, 15%);
}
- .diff-line-num.new, .line_content.new {
+ .diff-line-num.new,
+ .line_content.new {
@include diff_background(rgba(51, 255, 51, 0.1), rgba(51, 255, 51, 0.2), #808080);
}
- .diff-line-num.old, .line_content.old {
+ .diff-line-num.old,
+ .line_content.old {
@include diff_background(rgba(255, 51, 51, 0.2), rgba(255, 51, 51, 0.25), #808080);
}
@@ -55,68 +62,68 @@
color: #000 !important;
}
- .hll { background-color: #373b41 }
- .c { color: #969896 } /* Comment */
- .err { color: #c66 } /* Error */
- .k { color: #b294bb } /* Keyword */
- .l { color: #de935f } /* Literal */
- .n { color: #c5c8c6 } /* Name */
- .o { color: #8abeb7 } /* Operator */
- .p { color: #c5c8c6 } /* Punctuation */
- .cm { color: #969896 } /* Comment.Multiline */
- .cp { color: #969896 } /* Comment.Preproc */
- .c1 { color: #969896 } /* Comment.Single */
- .cs { color: #969896 } /* Comment.Special */
- .gd { color: #c66 } /* Generic.Deleted */
- .ge { font-style: italic } /* Generic.Emph */
- .gh { color: #c5c8c6; font-weight: bold } /* Generic.Heading */
- .gi { color: #b5bd68 } /* Generic.Inserted */
- .gp { color: #969896; font-weight: bold } /* Generic.Prompt */
- .gs { font-weight: bold } /* Generic.Strong */
- .gu { color: #8abeb7; font-weight: bold } /* Generic.Subheading */
- .kc { color: #b294bb } /* Keyword.Constant */
- .kd { color: #b294bb } /* Keyword.Declaration */
- .kn { color: #8abeb7 } /* Keyword.Namespace */
- .kp { color: #b294bb } /* Keyword.Pseudo */
- .kr { color: #b294bb } /* Keyword.Reserved */
- .kt { color: #f0c674 } /* Keyword.Type */
- .ld { color: #b5bd68 } /* Literal.Date */
- .m { color: #de935f } /* Literal.Number */
- .s { color: #b5bd68 } /* Literal.String */
- .na { color: #81a2be } /* Name.Attribute */
- .nb { color: #c5c8c6 } /* Name.Builtin */
- .nc { color: #f0c674 } /* Name.Class */
- .no { color: #c66 } /* Name.Constant */
- .nd { color: #8abeb7 } /* Name.Decorator */
- .ni { color: #c5c8c6 } /* Name.Entity */
- .ne { color: #c66 } /* Name.Exception */
- .nf { color: #81a2be } /* Name.Function */
- .nl { color: #c5c8c6 } /* Name.Label */
- .nn { color: #f0c674 } /* Name.Namespace */
- .nx { color: #81a2be } /* Name.Other */
- .py { color: #c5c8c6 } /* Name.Property */
- .nt { color: #8abeb7 } /* Name.Tag */
- .nv { color: #c66 } /* Name.Variable */
- .ow { color: #8abeb7 } /* Operator.Word */
- .w { color: #c5c8c6 } /* Text.Whitespace */
- .mf { color: #de935f } /* Literal.Number.Float */
- .mh { color: #de935f } /* Literal.Number.Hex */
- .mi { color: #de935f } /* Literal.Number.Integer */
- .mo { color: #de935f } /* Literal.Number.Oct */
- .sb { color: #b5bd68 } /* Literal.String.Backtick */
- .sc { color: #c5c8c6 } /* Literal.String.Char */
- .sd { color: #969896 } /* Literal.String.Doc */
- .s2 { color: #b5bd68 } /* Literal.String.Double */
- .se { color: #de935f } /* Literal.String.Escape */
- .sh { color: #b5bd68 } /* Literal.String.Heredoc */
- .si { color: #de935f } /* Literal.String.Interpol */
- .sx { color: #b5bd68 } /* Literal.String.Other */
- .sr { color: #b5bd68 } /* Literal.String.Regex */
- .s1 { color: #b5bd68 } /* Literal.String.Single */
- .ss { color: #b5bd68 } /* Literal.String.Symbol */
- .bp { color: #c5c8c6 } /* Name.Builtin.Pseudo */
- .vc { color: #c66 } /* Name.Variable.Class */
- .vg { color: #c66 } /* Name.Variable.Global */
- .vi { color: #c66 } /* Name.Variable.Instance */
- .il { color: #de935f } /* Literal.Number.Integer.Long */
+ .hll { background-color: #373b41; }
+ .c { color: #969896; } /* Comment */
+ .err { color: #c66; } /* Error */
+ .k { color: #b294bb; } /* Keyword */
+ .l { color: #de935f; } /* Literal */
+ .n { color: #c5c8c6; } /* Name */
+ .o { color: #8abeb7; } /* Operator */
+ .p { color: #c5c8c6; } /* Punctuation */
+ .cm { color: #969896; } /* Comment.Multiline */
+ .cp { color: #969896; } /* Comment.Preproc */
+ .c1 { color: #969896; } /* Comment.Single */
+ .cs { color: #969896; } /* Comment.Special */
+ .gd { color: #c66; } /* Generic.Deleted */
+ .ge { font-style: italic; } /* Generic.Emph */
+ .gh { color: #c5c8c6; font-weight: bold; } /* Generic.Heading */
+ .gi { color: #b5bd68; } /* Generic.Inserted */
+ .gp { color: #969896; font-weight: bold; } /* Generic.Prompt */
+ .gs { font-weight: bold; } /* Generic.Strong */
+ .gu { color: #8abeb7; font-weight: bold; } /* Generic.Subheading */
+ .kc { color: #b294bb; } /* Keyword.Constant */
+ .kd { color: #b294bb; } /* Keyword.Declaration */
+ .kn { color: #8abeb7; } /* Keyword.Namespace */
+ .kp { color: #b294bb; } /* Keyword.Pseudo */
+ .kr { color: #b294bb; } /* Keyword.Reserved */
+ .kt { color: #f0c674; } /* Keyword.Type */
+ .ld { color: #b5bd68; } /* Literal.Date */
+ .m { color: #de935f; } /* Literal.Number */
+ .s { color: #b5bd68; } /* Literal.String */
+ .na { color: #81a2be; } /* Name.Attribute */
+ .nb { color: #c5c8c6; } /* Name.Builtin */
+ .nc { color: #f0c674; } /* Name.Class */
+ .no { color: #c66; } /* Name.Constant */
+ .nd { color: #8abeb7; } /* Name.Decorator */
+ .ni { color: #c5c8c6; } /* Name.Entity */
+ .ne { color: #c66; } /* Name.Exception */
+ .nf { color: #81a2be; } /* Name.Function */
+ .nl { color: #c5c8c6; } /* Name.Label */
+ .nn { color: #f0c674; } /* Name.Namespace */
+ .nx { color: #81a2be; } /* Name.Other */
+ .py { color: #c5c8c6; } /* Name.Property */
+ .nt { color: #8abeb7; } /* Name.Tag */
+ .nv { color: #c66; } /* Name.Variable */
+ .ow { color: #8abeb7; } /* Operator.Word */
+ .w { color: #c5c8c6; } /* Text.Whitespace */
+ .mf { color: #de935f; } /* Literal.Number.Float */
+ .mh { color: #de935f; } /* Literal.Number.Hex */
+ .mi { color: #de935f; } /* Literal.Number.Integer */
+ .mo { color: #de935f; } /* Literal.Number.Oct */
+ .sb { color: #b5bd68; } /* Literal.String.Backtick */
+ .sc { color: #c5c8c6; } /* Literal.String.Char */
+ .sd { color: #969896; } /* Literal.String.Doc */
+ .s2 { color: #b5bd68; } /* Literal.String.Double */
+ .se { color: #de935f; } /* Literal.String.Escape */
+ .sh { color: #b5bd68; } /* Literal.String.Heredoc */
+ .si { color: #de935f; } /* Literal.String.Interpol */
+ .sx { color: #b5bd68; } /* Literal.String.Other */
+ .sr { color: #b5bd68; } /* Literal.String.Regex */
+ .s1 { color: #b5bd68; } /* Literal.String.Single */
+ .ss { color: #b5bd68; } /* Literal.String.Symbol */
+ .bp { color: #c5c8c6; } /* Name.Builtin.Pseudo */
+ .vc { color: #c66; } /* Name.Variable.Class */
+ .vg { color: #c66; } /* Name.Variable.Global */
+ .vi { color: #c66; } /* Name.Variable.Instance */
+ .il { color: #de935f; } /* Literal.Number.Integer.Long */
}
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index 7de920e074b..db8da8aab10 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -1,20 +1,25 @@
/* https://github.com/richleland/pygments-css/blob/master/monokai.css */
.code.monokai {
// Line numbers
- .line-numbers, .diff-line-num {
+ .line-numbers,
+ .diff-line-num {
background-color: #272822;
}
- .diff-line-num, .diff-line-num a {
+ .diff-line-num,
+ .diff-line-num a {
color: rgba(255, 255, 255, 0.3);
}
// Code itself
- pre.code, .diff-line-num {
+ pre.code,
+ .diff-line-num {
border-color: #555;
}
- &, pre.code, .line_holder .line_content {
+ &,
+ pre.code,
+ .line_holder .line_content {
background-color: #272822;
color: #f8f8f2;
}
@@ -31,11 +36,13 @@
border-color: darken(#49483e, 15%);
}
- .diff-line-num.new, .line_content.new {
+ .diff-line-num.new,
+ .line_content.new {
@include diff_background(rgba(166, 226, 46, 0.1), rgba(166, 226, 46, 0.15), #808080);
}
- .diff-line-num.old, .line_content.old {
+ .diff-line-num.old,
+ .line_content.old {
@include diff_background(rgba(254, 147, 140, 0.15), rgba(254, 147, 140, 0.2), #808080);
}
@@ -55,65 +62,65 @@
color: #000 !important;
}
- .hll { background-color: #49483e }
- .c { color: #75715e } /* Comment */
- .err { color: #960050; background-color: #1e0010 } /* Error */
- .k { color: #66d9ef } /* Keyword */
- .l { color: #ae81ff } /* Literal */
- .n { color: #f8f8f2 } /* Name */
- .o { color: #f92672 } /* Operator */
- .p { color: #f8f8f2 } /* Punctuation */
- .cm { color: #75715e } /* Comment.Multiline */
- .cp { color: #75715e } /* Comment.Preproc */
- .c1 { color: #75715e } /* Comment.Single */
- .cs { color: #75715e } /* Comment.Special */
- .ge { font-style: italic } /* Generic.Emph */
- .gs { font-weight: bold } /* Generic.Strong */
- .kc { color: #66d9ef } /* Keyword.Constant */
- .kd { color: #66d9ef } /* Keyword.Declaration */
- .kn { color: #f92672 } /* Keyword.Namespace */
- .kp { color: #66d9ef } /* Keyword.Pseudo */
- .kr { color: #66d9ef } /* Keyword.Reserved */
- .kt { color: #66d9ef } /* Keyword.Type */
- .ld { color: #e6db74 } /* Literal.Date */
- .m { color: #ae81ff } /* Literal.Number */
- .s { color: #e6db74 } /* Literal.String */
- .na { color: #a6e22e } /* Name.Attribute */
- .nb { color: #f8f8f2 } /* Name.Builtin */
- .nc { color: #a6e22e } /* Name.Class */
- .no { color: #66d9ef } /* Name.Constant */
- .nd { color: #a6e22e } /* Name.Decorator */
- .ni { color: #f8f8f2 } /* Name.Entity */
- .ne { color: #a6e22e } /* Name.Exception */
- .nf { color: #a6e22e } /* Name.Function */
- .nl { color: #f8f8f2 } /* Name.Label */
- .nn { color: #f8f8f2 } /* Name.Namespace */
- .nx { color: #a6e22e } /* Name.Other */
- .py { color: #f8f8f2 } /* Name.Property */
- .nt { color: #f92672 } /* Name.Tag */
- .nv { color: #f8f8f2 } /* Name.Variable */
- .ow { color: #f92672 } /* Operator.Word */
- .w { color: #f8f8f2 } /* Text.Whitespace */
- .mf { color: #ae81ff } /* Literal.Number.Float */
- .mh { color: #ae81ff } /* Literal.Number.Hex */
- .mi { color: #ae81ff } /* Literal.Number.Integer */
- .mo { color: #ae81ff } /* Literal.Number.Oct */
- .sb { color: #e6db74 } /* Literal.String.Backtick */
- .sc { color: #e6db74 } /* Literal.String.Char */
- .sd { color: #e6db74 } /* Literal.String.Doc */
- .s2 { color: #e6db74 } /* Literal.String.Double */
- .se { color: #ae81ff } /* Literal.String.Escape */
- .sh { color: #e6db74 } /* Literal.String.Heredoc */
- .si { color: #e6db74 } /* Literal.String.Interpol */
- .sx { color: #e6db74 } /* Literal.String.Other */
- .sr { color: #e6db74 } /* Literal.String.Regex */
- .s1 { color: #e6db74 } /* Literal.String.Single */
- .ss { color: #e6db74 } /* Literal.String.Symbol */
- .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
- .vc { color: #f8f8f2 } /* Name.Variable.Class */
- .vg { color: #f8f8f2 } /* Name.Variable.Global */
- .vi { color: #f8f8f2 } /* Name.Variable.Instance */
- .il { color: #ae81ff } /* Literal.Number.Integer.Long */
+ .hll { background-color: #49483e; }
+ .c { color: #75715e; } /* Comment */
+ .err { color: #960050; background-color: #1e0010; } /* Error */
+ .k { color: #66d9ef; } /* Keyword */
+ .l { color: #ae81ff; } /* Literal */
+ .n { color: #f8f8f2; } /* Name */
+ .o { color: #f92672; } /* Operator */
+ .p { color: #f8f8f2; } /* Punctuation */
+ .cm { color: #75715e; } /* Comment.Multiline */
+ .cp { color: #75715e; } /* Comment.Preproc */
+ .c1 { color: #75715e; } /* Comment.Single */
+ .cs { color: #75715e; } /* Comment.Special */
+ .ge { font-style: italic; } /* Generic.Emph */
+ .gs { font-weight: bold; } /* Generic.Strong */
+ .kc { color: #66d9ef; } /* Keyword.Constant */
+ .kd { color: #66d9ef; } /* Keyword.Declaration */
+ .kn { color: #f92672; } /* Keyword.Namespace */
+ .kp { color: #66d9ef; } /* Keyword.Pseudo */
+ .kr { color: #66d9ef; } /* Keyword.Reserved */
+ .kt { color: #66d9ef; } /* Keyword.Type */
+ .ld { color: #e6db74; } /* Literal.Date */
+ .m { color: #ae81ff; } /* Literal.Number */
+ .s { color: #e6db74; } /* Literal.String */
+ .na { color: #a6e22e; } /* Name.Attribute */
+ .nb { color: #f8f8f2; } /* Name.Builtin */
+ .nc { color: #a6e22e; } /* Name.Class */
+ .no { color: #66d9ef; } /* Name.Constant */
+ .nd { color: #a6e22e; } /* Name.Decorator */
+ .ni { color: #f8f8f2; } /* Name.Entity */
+ .ne { color: #a6e22e; } /* Name.Exception */
+ .nf { color: #a6e22e; } /* Name.Function */
+ .nl { color: #f8f8f2; } /* Name.Label */
+ .nn { color: #f8f8f2; } /* Name.Namespace */
+ .nx { color: #a6e22e; } /* Name.Other */
+ .py { color: #f8f8f2; } /* Name.Property */
+ .nt { color: #f92672; } /* Name.Tag */
+ .nv { color: #f8f8f2; } /* Name.Variable */
+ .ow { color: #f92672; } /* Operator.Word */
+ .w { color: #f8f8f2; } /* Text.Whitespace */
+ .mf { color: #ae81ff; } /* Literal.Number.Float */
+ .mh { color: #ae81ff; } /* Literal.Number.Hex */
+ .mi { color: #ae81ff; } /* Literal.Number.Integer */
+ .mo { color: #ae81ff; } /* Literal.Number.Oct */
+ .sb { color: #e6db74; } /* Literal.String.Backtick */
+ .sc { color: #e6db74; } /* Literal.String.Char */
+ .sd { color: #e6db74; } /* Literal.String.Doc */
+ .s2 { color: #e6db74; } /* Literal.String.Double */
+ .se { color: #ae81ff; } /* Literal.String.Escape */
+ .sh { color: #e6db74; } /* Literal.String.Heredoc */
+ .si { color: #e6db74; } /* Literal.String.Interpol */
+ .sx { color: #e6db74; } /* Literal.String.Other */
+ .sr { color: #e6db74; } /* Literal.String.Regex */
+ .s1 { color: #e6db74; } /* Literal.String.Single */
+ .ss { color: #e6db74; } /* Literal.String.Symbol */
+ .bp { color: #f8f8f2; } /* Name.Builtin.Pseudo */
+ .vc { color: #f8f8f2; } /* Name.Variable.Class */
+ .vg { color: #f8f8f2; } /* Name.Variable.Global */
+ .vi { color: #f8f8f2; } /* Name.Variable.Instance */
+ .il { color: #ae81ff; } /* Literal.Number.Integer.Long */
.gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */
.gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */
.gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index b11499c71ee..a87333146de 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -1,20 +1,25 @@
/* https://gist.github.com/qguv/7936275 */
.code.solarized-dark {
// Line numbers
- .line-numbers, .diff-line-num {
+ .line-numbers,
+ .diff-line-num {
background-color: #002b36;
}
- .diff-line-num, .diff-line-num a {
+ .diff-line-num,
+ .diff-line-num a {
color: rgba(255, 255, 255, 0.3);
}
// Code itself
- pre.code, .diff-line-num {
+ pre.code,
+ .diff-line-num {
border-color: #113b46;
}
- &, pre.code, .line_holder .line_content {
+ &,
+ pre.code,
+ .line_holder .line_content {
background-color: #002b36;
color: #93a1a1;
}
@@ -31,11 +36,13 @@
border-color: darken(#174652, 15%);
}
- .diff-line-num.new, .line_content.new {
+ .diff-line-num.new,
+ .line_content.new {
@include diff_background(rgba(133, 153, 0, 0.15), rgba(133, 153, 0, 0.25), #113b46);
}
- .diff-line-num.old, .line_content.old {
+ .diff-line-num.old,
+ .line_content.old {
@include diff_background(rgba(220, 50, 47, 0.3), rgba(220, 50, 47, 0.25), #113b46);
}
@@ -72,72 +79,72 @@
green #859900 operators, other keywords
*/
- .c { color: #586e75 } /* Comment */
- .err { color: #93a1a1 } /* Error */
- .g { color: #93a1a1 } /* Generic */
- .k { color: #859900 } /* Keyword */
- .l { color: #93a1a1 } /* Literal */
- .n { color: #93a1a1 } /* Name */
- .o { color: #859900 } /* Operator */
- .x { color: #cb4b16 } /* Other */
- .p { color: #93a1a1 } /* Punctuation */
- .cm { color: #586e75 } /* Comment.Multiline */
- .cp { color: #859900 } /* Comment.Preproc */
- .c1 { color: #586e75 } /* Comment.Single */
- .cs { color: #859900 } /* Comment.Special */
- .gd { color: #2aa198 } /* Generic.Deleted */
- .ge { color: #93a1a1; font-style: italic } /* Generic.Emph */
- .gr { color: #dc322f } /* Generic.Error */
- .gh { color: #cb4b16 } /* Generic.Heading */
- .gi { color: #859900 } /* Generic.Inserted */
- .go { color: #93a1a1 } /* Generic.Output */
- .gp { color: #93a1a1 } /* Generic.Prompt */
- .gs { color: #93a1a1; font-weight: bold } /* Generic.Strong */
- .gu { color: #cb4b16 } /* Generic.Subheading */
- .gt { color: #93a1a1 } /* Generic.Traceback */
- .kc { color: #cb4b16 } /* Keyword.Constant */
- .kd { color: #268bd2 } /* Keyword.Declaration */
- .kn { color: #859900 } /* Keyword.Namespace */
- .kp { color: #859900 } /* Keyword.Pseudo */
- .kr { color: #268bd2 } /* Keyword.Reserved */
- .kt { color: #dc322f } /* Keyword.Type */
- .ld { color: #93a1a1 } /* Literal.Date */
- .m { color: #2aa198 } /* Literal.Number */
- .s { color: #2aa198 } /* Literal.String */
- .na { color: #93a1a1 } /* Name.Attribute */
- .nb { color: #b58900 } /* Name.Builtin */
- .nc { color: #268bd2 } /* Name.Class */
- .no { color: #cb4b16 } /* Name.Constant */
- .nd { color: #268bd2 } /* Name.Decorator */
- .ni { color: #cb4b16 } /* Name.Entity */
- .ne { color: #cb4b16 } /* Name.Exception */
- .nf { color: #268bd2 } /* Name.Function */
- .nl { color: #93a1a1 } /* Name.Label */
- .nn { color: #93a1a1 } /* Name.Namespace */
- .nx { color: #93a1a1 } /* Name.Other */
- .py { color: #93a1a1 } /* Name.Property */
- .nt { color: #268bd2 } /* Name.Tag */
- .nv { color: #268bd2 } /* Name.Variable */
- .ow { color: #859900 } /* Operator.Word */
- .w { color: #93a1a1 } /* Text.Whitespace */
- .mf { color: #2aa198 } /* Literal.Number.Float */
- .mh { color: #2aa198 } /* Literal.Number.Hex */
- .mi { color: #2aa198 } /* Literal.Number.Integer */
- .mo { color: #2aa198 } /* Literal.Number.Oct */
- .sb { color: #586e75 } /* Literal.String.Backtick */
- .sc { color: #2aa198 } /* Literal.String.Char */
- .sd { color: #93a1a1 } /* Literal.String.Doc */
- .s2 { color: #2aa198 } /* Literal.String.Double */
- .se { color: #cb4b16 } /* Literal.String.Escape */
- .sh { color: #93a1a1 } /* Literal.String.Heredoc */
- .si { color: #2aa198 } /* Literal.String.Interpol */
- .sx { color: #2aa198 } /* Literal.String.Other */
- .sr { color: #dc322f } /* Literal.String.Regex */
- .s1 { color: #2aa198 } /* Literal.String.Single */
- .ss { color: #2aa198 } /* Literal.String.Symbol */
- .bp { color: #268bd2 } /* Name.Builtin.Pseudo */
- .vc { color: #268bd2 } /* Name.Variable.Class */
- .vg { color: #268bd2 } /* Name.Variable.Global */
- .vi { color: #268bd2 } /* Name.Variable.Instance */
- .il { color: #2aa198 } /* Literal.Number.Integer.Long */
+ .c { color: #586e75; } /* Comment */
+ .err { color: #93a1a1; } /* Error */
+ .g { color: #93a1a1; } /* Generic */
+ .k { color: #859900; } /* Keyword */
+ .l { color: #93a1a1; } /* Literal */
+ .n { color: #93a1a1; } /* Name */
+ .o { color: #859900; } /* Operator */
+ .x { color: #cb4b16; } /* Other */
+ .p { color: #93a1a1; } /* Punctuation */
+ .cm { color: #586e75; } /* Comment.Multiline */
+ .cp { color: #859900; } /* Comment.Preproc */
+ .c1 { color: #586e75; } /* Comment.Single */
+ .cs { color: #859900; } /* Comment.Special */
+ .gd { color: #2aa198; } /* Generic.Deleted */
+ .ge { color: #93a1a1; font-style: italic; } /* Generic.Emph */
+ .gr { color: #dc322f; } /* Generic.Error */
+ .gh { color: #cb4b16; } /* Generic.Heading */
+ .gi { color: #859900; } /* Generic.Inserted */
+ .go { color: #93a1a1; } /* Generic.Output */
+ .gp { color: #93a1a1; } /* Generic.Prompt */
+ .gs { color: #93a1a1; font-weight: bold; } /* Generic.Strong */
+ .gu { color: #cb4b16; } /* Generic.Subheading */
+ .gt { color: #93a1a1; } /* Generic.Traceback */
+ .kc { color: #cb4b16; } /* Keyword.Constant */
+ .kd { color: #268bd2; } /* Keyword.Declaration */
+ .kn { color: #859900; } /* Keyword.Namespace */
+ .kp { color: #859900; } /* Keyword.Pseudo */
+ .kr { color: #268bd2; } /* Keyword.Reserved */
+ .kt { color: #dc322f; } /* Keyword.Type */
+ .ld { color: #93a1a1; } /* Literal.Date */
+ .m { color: #2aa198; } /* Literal.Number */
+ .s { color: #2aa198; } /* Literal.String */
+ .na { color: #93a1a1; } /* Name.Attribute */
+ .nb { color: #b58900; } /* Name.Builtin */
+ .nc { color: #268bd2; } /* Name.Class */
+ .no { color: #cb4b16; } /* Name.Constant */
+ .nd { color: #268bd2; } /* Name.Decorator */
+ .ni { color: #cb4b16; } /* Name.Entity */
+ .ne { color: #cb4b16; } /* Name.Exception */
+ .nf { color: #268bd2; } /* Name.Function */
+ .nl { color: #93a1a1; } /* Name.Label */
+ .nn { color: #93a1a1; } /* Name.Namespace */
+ .nx { color: #93a1a1; } /* Name.Other */
+ .py { color: #93a1a1; } /* Name.Property */
+ .nt { color: #268bd2; } /* Name.Tag */
+ .nv { color: #268bd2; } /* Name.Variable */
+ .ow { color: #859900; } /* Operator.Word */
+ .w { color: #93a1a1; } /* Text.Whitespace */
+ .mf { color: #2aa198; } /* Literal.Number.Float */
+ .mh { color: #2aa198; } /* Literal.Number.Hex */
+ .mi { color: #2aa198; } /* Literal.Number.Integer */
+ .mo { color: #2aa198; } /* Literal.Number.Oct */
+ .sb { color: #586e75; } /* Literal.String.Backtick */
+ .sc { color: #2aa198; } /* Literal.String.Char */
+ .sd { color: #93a1a1; } /* Literal.String.Doc */
+ .s2 { color: #2aa198; } /* Literal.String.Double */
+ .se { color: #cb4b16; } /* Literal.String.Escape */
+ .sh { color: #93a1a1; } /* Literal.String.Heredoc */
+ .si { color: #2aa198; } /* Literal.String.Interpol */
+ .sx { color: #2aa198; } /* Literal.String.Other */
+ .sr { color: #dc322f; } /* Literal.String.Regex */
+ .s1 { color: #2aa198; } /* Literal.String.Single */
+ .ss { color: #2aa198; } /* Literal.String.Symbol */
+ .bp { color: #268bd2; } /* Name.Builtin.Pseudo */
+ .vc { color: #268bd2; } /* Name.Variable.Class */
+ .vg { color: #268bd2; } /* Name.Variable.Global */
+ .vi { color: #268bd2; } /* Name.Variable.Instance */
+ .il { color: #2aa198; } /* Literal.Number.Integer.Long */
}
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index 657bb5e3cd9..faff353ded7 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -7,20 +7,25 @@
.code.solarized-light {
// Line numbers
- .line-numbers, .diff-line-num {
+ .line-numbers,
+ .diff-line-num {
background-color: #fdf6e3;
}
- .diff-line-num, .diff-line-num a {
+ .diff-line-num,
+ .diff-line-num a {
color: $black-transparent;
}
// Code itself
- pre.code, .diff-line-num {
+ pre.code,
+ .diff-line-num {
border-color: #c5d0d4;
}
- &, pre.code, .line_holder .line_content {
+ &,
+ pre.code,
+ .line_holder .line_content {
background-color: #fdf6e3;
color: #586e75;
}
@@ -37,11 +42,13 @@
border-color: darken(#ddd8c5, 15%);
}
- .diff-line-num.new, .line_content.new {
+ .diff-line-num.new,
+ .line_content.new {
@include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.25), #c5d0d4);
}
- .diff-line-num.old, .line_content.old {
+ .diff-line-num.old,
+ .line_content.old {
@include diff_background(rgba(220, 50, 47, 0.2), rgba(220, 50, 47, 0.25), #c5d0d4);
}
@@ -78,72 +85,72 @@
green #859900 operators, other keywords
*/
- .c { color: #93a1a1 } /* Comment */
- .err { color: #586e75 } /* Error */
- .g { color: #586e75 } /* Generic */
- .k { color: #859900 } /* Keyword */
- .l { color: #586e75 } /* Literal */
- .n { color: #586e75 } /* Name */
- .o { color: #859900 } /* Operator */
- .x { color: #cb4b16 } /* Other */
- .p { color: #586e75 } /* Punctuation */
- .cm { color: #93a1a1 } /* Comment.Multiline */
- .cp { color: #859900 } /* Comment.Preproc */
- .c1 { color: #93a1a1 } /* Comment.Single */
- .cs { color: #859900 } /* Comment.Special */
- .gd { color: #2aa198 } /* Generic.Deleted */
- .ge { color: #586e75; font-style: italic } /* Generic.Emph */
- .gr { color: #dc322f } /* Generic.Error */
- .gh { color: #cb4b16 } /* Generic.Heading */
- .gi { color: #859900 } /* Generic.Inserted */
- .go { color: #586e75 } /* Generic.Output */
- .gp { color: #586e75 } /* Generic.Prompt */
- .gs { color: #586e75; font-weight: bold } /* Generic.Strong */
- .gu { color: #cb4b16 } /* Generic.Subheading */
- .gt { color: #586e75 } /* Generic.Traceback */
- .kc { color: #cb4b16 } /* Keyword.Constant */
- .kd { color: #268bd2 } /* Keyword.Declaration */
- .kn { color: #859900 } /* Keyword.Namespace */
- .kp { color: #859900 } /* Keyword.Pseudo */
- .kr { color: #268bd2 } /* Keyword.Reserved */
- .kt { color: #dc322f } /* Keyword.Type */
- .ld { color: #586e75 } /* Literal.Date */
- .m { color: #2aa198 } /* Literal.Number */
- .s { color: #2aa198 } /* Literal.String */
- .na { color: #586e75 } /* Name.Attribute */
- .nb { color: #b58900 } /* Name.Builtin */
- .nc { color: #268bd2 } /* Name.Class */
- .no { color: #cb4b16 } /* Name.Constant */
- .nd { color: #268bd2 } /* Name.Decorator */
- .ni { color: #cb4b16 } /* Name.Entity */
- .ne { color: #cb4b16 } /* Name.Exception */
- .nf { color: #268bd2 } /* Name.Function */
- .nl { color: #586e75 } /* Name.Label */
- .nn { color: #586e75 } /* Name.Namespace */
- .nx { color: #586e75 } /* Name.Other */
- .py { color: #586e75 } /* Name.Property */
- .nt { color: #268bd2 } /* Name.Tag */
- .nv { color: #268bd2 } /* Name.Variable */
- .ow { color: #859900 } /* Operator.Word */
- .w { color: #586e75 } /* Text.Whitespace */
- .mf { color: #2aa198 } /* Literal.Number.Float */
- .mh { color: #2aa198 } /* Literal.Number.Hex */
- .mi { color: #2aa198 } /* Literal.Number.Integer */
- .mo { color: #2aa198 } /* Literal.Number.Oct */
- .sb { color: #93a1a1 } /* Literal.String.Backtick */
- .sc { color: #2aa198 } /* Literal.String.Char */
- .sd { color: #586e75 } /* Literal.String.Doc */
- .s2 { color: #2aa198 } /* Literal.String.Double */
- .se { color: #cb4b16 } /* Literal.String.Escape */
- .sh { color: #586e75 } /* Literal.String.Heredoc */
- .si { color: #2aa198 } /* Literal.String.Interpol */
- .sx { color: #2aa198 } /* Literal.String.Other */
- .sr { color: #dc322f } /* Literal.String.Regex */
- .s1 { color: #2aa198 } /* Literal.String.Single */
- .ss { color: #2aa198 } /* Literal.String.Symbol */
- .bp { color: #268bd2 } /* Name.Builtin.Pseudo */
- .vc { color: #268bd2 } /* Name.Variable.Class */
- .vg { color: #268bd2 } /* Name.Variable.Global */
- .vi { color: #268bd2 } /* Name.Variable.Instance */
- .il { color: #2aa198 } /* Literal.Number.Integer.Long */
+ .c { color: #93a1a1; } /* Comment */
+ .err { color: #586e75; } /* Error */
+ .g { color: #586e75; } /* Generic */
+ .k { color: #859900; } /* Keyword */
+ .l { color: #586e75; } /* Literal */
+ .n { color: #586e75; } /* Name */
+ .o { color: #859900; } /* Operator */
+ .x { color: #cb4b16; } /* Other */
+ .p { color: #586e75; } /* Punctuation */
+ .cm { color: #93a1a1; } /* Comment.Multiline */
+ .cp { color: #859900; } /* Comment.Preproc */
+ .c1 { color: #93a1a1; } /* Comment.Single */
+ .cs { color: #859900; } /* Comment.Special */
+ .gd { color: #2aa198; } /* Generic.Deleted */
+ .ge { color: #586e75; font-style: italic; } /* Generic.Emph */
+ .gr { color: #dc322f; } /* Generic.Error */
+ .gh { color: #cb4b16; } /* Generic.Heading */
+ .gi { color: #859900; } /* Generic.Inserted */
+ .go { color: #586e75; } /* Generic.Output */
+ .gp { color: #586e75; } /* Generic.Prompt */
+ .gs { color: #586e75; font-weight: bold; } /* Generic.Strong */
+ .gu { color: #cb4b16; } /* Generic.Subheading */
+ .gt { color: #586e75; } /* Generic.Traceback */
+ .kc { color: #cb4b16; } /* Keyword.Constant */
+ .kd { color: #268bd2; } /* Keyword.Declaration */
+ .kn { color: #859900; } /* Keyword.Namespace */
+ .kp { color: #859900; } /* Keyword.Pseudo */
+ .kr { color: #268bd2; } /* Keyword.Reserved */
+ .kt { color: #dc322f; } /* Keyword.Type */
+ .ld { color: #586e75; } /* Literal.Date */
+ .m { color: #2aa198; } /* Literal.Number */
+ .s { color: #2aa198; } /* Literal.String */
+ .na { color: #586e75; } /* Name.Attribute */
+ .nb { color: #b58900; } /* Name.Builtin */
+ .nc { color: #268bd2; } /* Name.Class */
+ .no { color: #cb4b16; } /* Name.Constant */
+ .nd { color: #268bd2; } /* Name.Decorator */
+ .ni { color: #cb4b16; } /* Name.Entity */
+ .ne { color: #cb4b16; } /* Name.Exception */
+ .nf { color: #268bd2; } /* Name.Function */
+ .nl { color: #586e75; } /* Name.Label */
+ .nn { color: #586e75; } /* Name.Namespace */
+ .nx { color: #586e75; } /* Name.Other */
+ .py { color: #586e75; } /* Name.Property */
+ .nt { color: #268bd2; } /* Name.Tag */
+ .nv { color: #268bd2; } /* Name.Variable */
+ .ow { color: #859900; } /* Operator.Word */
+ .w { color: #586e75; } /* Text.Whitespace */
+ .mf { color: #2aa198; } /* Literal.Number.Float */
+ .mh { color: #2aa198; } /* Literal.Number.Hex */
+ .mi { color: #2aa198; } /* Literal.Number.Integer */
+ .mo { color: #2aa198; } /* Literal.Number.Oct */
+ .sb { color: #93a1a1; } /* Literal.String.Backtick */
+ .sc { color: #2aa198; } /* Literal.String.Char */
+ .sd { color: #586e75; } /* Literal.String.Doc */
+ .s2 { color: #2aa198; } /* Literal.String.Double */
+ .se { color: #cb4b16; } /* Literal.String.Escape */
+ .sh { color: #586e75; } /* Literal.String.Heredoc */
+ .si { color: #2aa198; } /* Literal.String.Interpol */
+ .sx { color: #2aa198; } /* Literal.String.Other */
+ .sr { color: #dc322f; } /* Literal.String.Regex */
+ .s1 { color: #2aa198; } /* Literal.String.Single */
+ .ss { color: #2aa198; } /* Literal.String.Symbol */
+ .bp { color: #268bd2; } /* Name.Builtin.Pseudo */
+ .vc { color: #268bd2; } /* Name.Variable.Class */
+ .vg { color: #268bd2; } /* Name.Variable.Global */
+ .vi { color: #268bd2; } /* Name.Variable.Instance */
+ .il { color: #2aa198; } /* Literal.Number.Integer.Long */
}
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 36a80a916b2..d5367d5f3f0 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -7,20 +7,25 @@
.code.white {
// Line numbers
- .line-numbers, .diff-line-num {
+ .line-numbers,
+ .diff-line-num {
background-color: $background-color;
}
- .diff-line-num, .diff-line-num a {
+ .diff-line-num,
+ .diff-line-num a {
color: $black-transparent;
}
// Code itself
- pre.code, .diff-line-num {
+ pre.code,
+ .diff-line-num {
border-color: $table-border-gray;
}
- &, pre.code, .line_holder .line_content {
+ &,
+ pre.code,
+ .line_holder .line_content {
background-color: #fff;
color: #333;
}
@@ -86,7 +91,7 @@
background-color: #fafe3d !important;
}
- .hll { background-color: #f8f8f8 }
+ .hll { background-color: #f8f8f8; }
.c { color: #998; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
.k { font-weight: bold; }
diff --git a/app/assets/stylesheets/mailers/devise.scss b/app/assets/stylesheets/mailers/devise.scss
index 9495c5b3f37..b2bce482fde 100644
--- a/app/assets/stylesheets/mailers/devise.scss
+++ b/app/assets/stylesheets/mailers/devise.scss
@@ -5,13 +5,13 @@
// Styles defined here are embedded directly into the resulting email HTML via
// the `premailer` gem.
-$body-background-color: #363636;
+$body-background-color: #363636;
$message-background-color: #fafafa;
-$header-color: #6b4fbb;
-$body-color: #444;
-$cta-color: #e14329;
-$footer-link-color: #7e7e7e;
+$header-color: #6b4fbb;
+$body-color: #444;
+$cta-color: #e14329;
+$footer-link-color: #7e7e7e;
$font-family: Helvetica, Arial, sans-serif;
diff --git a/app/assets/stylesheets/mailers/repository_push_email.scss b/app/assets/stylesheets/mailers/repository_push_email.scss
index 5bfe9bcb443..8d1a6020ca4 100644
--- a/app/assets/stylesheets/mailers/repository_push_email.scss
+++ b/app/assets/stylesheets/mailers/repository_push_email.scss
@@ -78,7 +78,7 @@ span.highlight_word {
background-color: #fafe3d !important;
}
-.hll { background-color: #f8f8f8 }
+.hll { background-color: #f8f8f8; }
.c { color: #998; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
.k { font-weight: bold; }
diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss
index fc12964872d..ced8c4a9907 100644
--- a/app/assets/stylesheets/notify.scss
+++ b/app/assets/stylesheets/notify.scss
@@ -2,22 +2,28 @@ img {
max-width: 100%;
height: auto;
}
+
p.details {
font-style: italic;
- color: #777
+ color: #777;
}
+
.footer > p {
font-size: small;
- color: #777
+ color: #777;
}
+
pre.commit-message {
white-space: pre-wrap;
}
+
.file-stats > a {
text-decoration: none;
+
> .new-file {
color: #090;
}
+
> .deleted-file {
color: #b00;
}
diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss
index 8f71381f5c4..14812e171fd 100644
--- a/app/assets/stylesheets/pages/admin.scss
+++ b/app/assets/stylesheets/pages/admin.scss
@@ -22,7 +22,7 @@
.admin-filter form {
.select2-container {
- width: 100%
+ width: 100%;
}
.controls {
@@ -31,7 +31,7 @@
.form-actions {
padding-left: 130px;
- background: #fff
+ background: #fff;
}
.visibility-levels {
@@ -56,7 +56,8 @@
padding: 10px;
text-align: center;
- > div, p {
+ > div,
+ p {
display: inline;
margin: 0;
@@ -79,10 +80,13 @@
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
+ white-space: nowrap;
}
.user-details {
flex: 1 1 auto;
+ overflow: hidden;
+ padding-right: 8px;
}
.user-name {
@@ -90,6 +94,12 @@
font-weight: 600;
}
+ .user-name,
+ .user-email {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
.dropdown {
.btn-block {
margin-bottom: 0;
@@ -106,26 +116,33 @@
.table {
table-layout: fixed;
}
+
.subheading {
padding-bottom: $gl-padding;
}
+
.message {
word-wrap: break-word;
}
+
.btn {
white-space: normal;
padding: $gl-btn-padding;
}
+
th {
width: 15%;
+
&.wide {
width: 55%;
}
}
+
@media (max-width: $screen-sm-max) {
th {
width: 100%;
}
+
td {
width: 100%;
float: left;
@@ -137,8 +154,15 @@
margin-left: $btn-side-margin;
margin-top: 3px;
}
+
span {
font-size: 19px;
}
}
}
+
+.admin-builds-table {
+ .ci-table td:last-child {
+ min-width: 120px;
+ }
+}
diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss
index 9282e0ae03b..486ad16ea26 100644
--- a/app/assets/stylesheets/pages/awards.scss
+++ b/app/assets/stylesheets/pages/awards.scss
@@ -1,7 +1,7 @@
.awards {
.emoji-icon {
- width: 20px;
- height: 20px;
+ width: 19px;
+ height: 19px;
}
}
@@ -94,7 +94,7 @@
.award-control {
margin: 3px 5px 3px 0;
- padding: 6px 5px;
+ padding: 5px 6px;
outline: 0;
&:hover,
@@ -127,7 +127,7 @@
.award-control-icon {
float: left;
margin-right: 5px;
- font-size: 20px;
+ font-size: 19px;
}
.award-control-icon-loading {
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 037278bb083..4327f8bf640 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -12,12 +12,20 @@
opacity: 1!important;
* {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
// !important to make sure no style can override this when dragging
cursor: -webkit-grabbing!important;
cursor: grabbing!important;
}
}
+.is-ghost {
+ opacity: 0.3;
+}
+
.dropdown-menu-issues-board-new {
width: 320px;
@@ -34,47 +42,17 @@
> p {
margin: 0;
font-size: 14px;
- color: #9c9c9c;
}
}
.issue-boards-page {
- .content-wrapper {
- display: -webkit-flex;
- display: flex;
- -webkit-flex-direction: column;
- flex-direction: column;
- }
-
- .sub-nav,
- .issues-filters {
- -webkit-flex: none;
- flex: none;
- }
-
.page-with-sidebar {
- display: -webkit-flex;
- display: flex;
- min-height: 100vh;
- max-height: 100vh;
padding-bottom: 0;
}
+}
- .issue-boards-content {
- display: -webkit-flex;
- display: flex;
- -webkit-flex: 1;
- flex: 1;
- width: 100%;
-
- .content {
- display: -webkit-flex;
- display: flex;
- -webkit-flex-direction: column;
- flex-direction: column;
- width: 100%;
- }
- }
+.boards-app {
+ position: relative;
}
.boards-app-loading {
@@ -83,46 +61,42 @@
}
.boards-list {
- display: -webkit-flex;
- display: flex;
- -webkit-flex: 1;
- flex: 1;
- -webkit-flex-basis: 0;
- flex-basis: 0;
- min-height: calc(100vh - 152px);
- max-height: calc(100vh - 152px);
+ height: calc(100vh - 152px);
+ width: 100%;
padding-top: 25px;
+ padding-bottom: 25px;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
overflow-x: scroll;
+ white-space: nowrap;
@media (min-width: $screen-sm-min) {
+ height: 475px; // Needed for PhantomJS
+ height: calc(100vh - 220px);
min-height: 475px;
- max-height: none;
+
+ &.is-compact {
+ width: calc(100% - 290px);
+ }
}
}
.board {
- display: -webkit-flex;
- display: flex;
- min-width: calc(85vw - 15px);
- max-width: calc(85vw - 15px);
- margin-bottom: 25px;
+ display: inline-block;
+ width: calc(85vw - 15px);
+ height: 100%;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
+ white-space: normal;
+ vertical-align: top;
@media (min-width: $screen-sm-min) {
- min-width: 400px;
- max-width: 400px;
+ width: 400px;
}
}
.board-inner {
- display: -webkit-flex;
- display: flex;
- -webkit-flex-direction: column;
- flex-direction: column;
- width: 100%;
+ height: 100%;
font-size: $issue-boards-font-size;
background: $background-color;
border: 1px solid $border-color;
@@ -169,7 +143,7 @@
}
.board-blank-state {
- height: 100%;
+ height: calc(100% - 49px);
padding: $gl-padding;
background-color: #fff;
}
@@ -192,44 +166,42 @@
}
}
+.board-list-component {
+ height: calc(100% - 49px);
+}
+
.board-list {
- -webkit-flex: 1;
- flex: 1;
- height: 400px;
+ height: 100%;
margin-bottom: 0;
padding: 5px;
+ list-style: none;
overflow-y: scroll;
overflow-x: hidden;
+
+ &.is-smaller {
+ height: calc(100% - 136px);
+ }
}
.board-list-loading {
margin-top: 10px;
- font-size: 26px;
-}
-
-.is-ghost {
- opacity: 0.3;
+ font-size: (26px / $issue-boards-font-size) * 1em;
}
.card {
position: relative;
- width: 100%;
padding: 10px $gl-padding;
background: #fff;
border-radius: $border-radius-default;
box-shadow: 0 1px 2px rgba(186, 186, 186, 0.5);
list-style: none;
- &.user-can-drag {
- padding-left: $gl-padding;
- }
-
&:not(:last-child) {
margin-bottom: 5px;
}
- a {
- cursor: pointer;
+ &.is-active {
+ background-color: $row-hover;
}
.label {
@@ -248,6 +220,7 @@
a {
color: inherit;
+ word-wrap: break-word;
}
}
@@ -256,18 +229,21 @@
line-height: 25px;
.label {
- margin-right: 4px;
+ margin-right: 5px;
font-size: (14px / $issue-boards-font-size) * 1em;
}
+
+ .avatar {
+ margin-left: 0;
+ }
}
.card-number {
- margin-right: 8px;
- font-weight: 500;
+ margin-right: 5px;
}
.issue-boards-search {
- width: 335px;
+ width: 290px;
.form-control {
display: inline-block;
@@ -284,3 +260,76 @@
margin-right: 5px;
}
}
+
+.board-new-issue-form {
+ margin: 5px;
+}
+
+.board-issue-count-holder {
+ margin-top: -3px;
+
+ .btn {
+ line-height: 12px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+}
+
+.board-issue-count {
+ padding-right: 10px;
+ padding-left: 10px;
+ line-height: 21px;
+ border-radius: $border-radius-base;
+ border: 1px solid $border-color;
+
+ &.has-btn {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-width: 1px 0 1px 1px;
+ }
+}
+
+.issue-boards-sidebar {
+ &.right-sidebar {
+ top: 153px;
+ bottom: 0;
+
+ @media (min-width: $screen-sm-min) {
+ top: 220px;
+ }
+ }
+
+ .issuable-sidebar-header {
+ position: relative;
+ }
+
+ .gutter-toggle {
+ position: absolute;
+ top: 0;
+ bottom: 15px;
+ right: 0;
+ width: 22px;
+ color: $gray-darkest;
+
+ svg {
+ position: absolute;
+ top: 50%;
+ margin-top: (-11px / 2);
+ }
+
+ &:hover {
+ path {
+ fill: $gray-darkest;
+ }
+ }
+ }
+
+ .issuable-header-text {
+ width: 100%;
+ padding-right: 35px;
+
+ > strong {
+ font-weight: 600;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index c879074c7fe..48f11eb2552 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -14,18 +14,10 @@
}
}
- .autoscroll-container {
- position: fixed;
- bottom: 20px;
- right: 20px;
- z-index: 100;
- }
-
.scroll-controls {
- &.affix-top {
- position: absolute;
- top: 10px;
- right: 25px;
+ .scroll-step {
+ width: 31px;
+ margin: 0 0 0 auto;
}
&.affix-bottom {
@@ -34,13 +26,13 @@
}
&.affix {
- right: 30px;
+ right: 25px;
bottom: 15px;
z-index: 1;
+ }
- @media (min-width: $screen-md-min) {
- right: 26%;
- }
+ &.sidebar-expanded {
+ right: #{$gutter_width + ($gl-padding * 2)};
}
a {
@@ -48,22 +40,46 @@
margin-bottom: 10px;
}
}
+
+ .environment-information {
+ background-color: $background-color;
+ border: 1px solid $border-color;
+ padding: 12px $gl-padding;
+ border-radius: $border-radius-default;
+
+ svg {
+ position: relative;
+ top: 1px;
+ margin-right: 5px;
+ }
+ }
}
.build-header {
position: relative;
- padding-right: 40px;
+ padding: 0;
+ display: flex;
+ min-height: 58px;
+ align-items: center;
- @media (min-width: $screen-sm-min) {
- padding-right: 0;
+ @media (max-width: $screen-sm-max) {
+ padding-right: 40px;
+
+ .btn-inverted {
+ display: none;
+ }
}
- a {
- color: $gl-gray;
+ .header-content {
+ flex: 1;
- &:hover {
- color: $gl-link-color;
- text-decoration: none;
+ a {
+ color: $gl-gray;
+
+ &:hover {
+ color: $gl-link-color;
+ text-decoration: none;
+ }
}
}
@@ -107,6 +123,14 @@
.block {
width: 100%;
+
+ &.coverage {
+ padding: 0 16px 11px;
+ }
+
+ .btn-group-justified {
+ margin-top: 5px;
+ }
}
.js-build-variable {
@@ -129,9 +153,15 @@
.retry-link {
color: $gl-link-color;
+ display: none;
+
&:hover {
text-decoration: underline;
}
+
+ @media (max-width: $screen-sm-max) {
+ display: block;
+ }
}
.stage-item {
@@ -186,7 +216,7 @@
.build-job {
position: relative;
- .fa {
+ .fa-arrow-right {
position: absolute;
left: 15px;
top: 20px;
@@ -196,20 +226,33 @@
&.active {
font-weight: bold;
- .fa {
+ .fa-arrow-right {
display: block;
}
}
+ &.retried {
+ background-color: $gray-lightest;
+ }
+
&:hover {
background-color: $row-hover;
}
+
+ .fa-refresh {
+ font-size: 13px;
+ margin-left: 3px;
+ }
}
}
}
.build-detail-row {
margin-bottom: 5px;
+
+ &:last-of-type {
+ margin-bottom: 0;
+ }
}
.build-light-text {
@@ -222,3 +265,9 @@
right: 0;
margin-top: -17px;
}
+
+@media (min-width: $screen-md-min) {
+ .sub-nav.build {
+ width: calc(100% + #{$gutter_width});
+ }
+}
diff --git a/app/assets/stylesheets/pages/ci_projects.scss b/app/assets/stylesheets/pages/ci_projects.scss
index 67a9d7d2cf7..87c453a7a27 100644
--- a/app/assets/stylesheets/pages/ci_projects.scss
+++ b/app/assets/stylesheets/pages/ci_projects.scss
@@ -12,7 +12,8 @@
border-color: $border-color;
}
- th, td {
+ th,
+ td {
padding: 10px $gl-padding;
}
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 53ec0002afe..ddc9d0e2b1a 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -2,14 +2,16 @@
display: block;
}
-.commit-author, .commit-committer {
+.commit-author,
+.commit-committer {
display: block;
color: #999;
font-weight: normal;
font-style: italic;
}
-.commit-author strong, .commit-committer strong {
+.commit-author strong,
+.commit-committer strong {
font-weight: bold;
font-style: normal;
}
@@ -24,56 +26,9 @@
white-space: pre-wrap;
}
-.commit-info-row {
- margin-bottom: 10px;
- line-height: 24px;
- padding-top: 6px;
-
- &.commit-info-row-header {
- line-height: 34px;
-
- @media (min-width: $screen-sm-min) {
- margin-bottom: 0;
- }
-
- .commit-options-dropdown-caret {
- @media (max-width: $screen-sm) {
- margin-left: 0;
- }
- }
- }
-
- .avatar {
- @extend .avatar-inline;
- margin-left: 0;
-
- @media (min-width: $screen-sm-min) {
- margin-left: 4px;
- }
- }
- .commit-committer-link,
- .commit-author-link {
- color: $gl-gray;
- font-weight: bold;
- }
-
- .fa-clipboard {
- color: $dropdown-title-btn-color;
- }
-
- .commit-info {
- &.branches {
- margin-left: 8px;
- }
- }
-
- .ci-status-link {
-
- svg {
- position: relative;
- top: 2px;
- margin: 0 2px 0 3px;
- }
+.js-details-expand {
+ &:hover {
+ text-decoration: none;
}
}
@@ -85,6 +40,7 @@
.commit-box {
border-top: 1px solid $border-color;
+ padding: $gl-padding 0;
.commit-title {
margin: 0;
@@ -97,6 +53,17 @@
}
}
+.commit-hash-full {
+ @media (max-width: $screen-sm-max) {
+ width: 80px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ vertical-align: bottom;
+ }
+}
+
.file-stats {
ul {
list-style: none;
@@ -108,21 +75,25 @@
line-height: 20px;
}
}
+
.new-file {
a {
color: $gl-text-green;
}
}
+
.renamed-file {
a {
color: $gl-text-orange;
}
}
+
.deleted-file {
a {
color: $gl-text-red;
}
}
+
.edit-file {
a {
color: $gl-text-color;
@@ -130,14 +101,6 @@
}
}
-.commit-action-buttons {
- i {
- color: $gl-icon-color;
- font-size: 13px;
- margin-right: 3px;
- }
-}
-
/*
* Commit message textarea for web editor and
* custom merge request message
@@ -158,6 +121,7 @@
position: absolute;
z-index: 1;
}
+
> textarea {
background-color: rgba(0, 0, 0, 0.0);
font-family: inherit;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index dc57a837155..83ffa0e1d39 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -33,21 +33,22 @@
color: $gl-dark-link-color;
}
- .text-expander {
- display: inline-block;
- background: $gray-light;
- color: $gl-placeholder-color;
- padding: 0 5px;
- cursor: pointer;
- border: 1px solid $border-gray-dark;
- border-radius: $border-radius-default;
- margin-left: 5px;
- line-height: 1;
-
- &:hover {
- background-color: darken($gray-light, 10%);
- text-decoration: none;
- }
+}
+
+.text-expander {
+ display: inline-block;
+ background: $gray-light;
+ color: $gl-placeholder-color;
+ padding: 0 5px;
+ cursor: pointer;
+ border: 1px solid $border-gray-dark;
+ border-radius: $border-radius-default;
+ margin-left: 5px;
+ line-height: 1;
+
+ &:hover {
+ background-color: darken($gray-light, 10%);
+ text-decoration: none;
}
}
@@ -61,9 +62,12 @@
.ci-status-link {
display: inline-block;
+ position: relative;
+ top: 1px;
}
- .btn-clipboard, .btn-transparent {
+ .btn-clipboard,
+ .btn-transparent {
padding-left: 0;
padding-right: 0;
}
@@ -80,7 +84,8 @@
font-weight: 600;
}
-.commit {
+.commit,
+.generic_commit_status {
padding: 10px 0;
position: relative;
@@ -98,7 +103,6 @@
vertical-align: baseline;
}
-
.avatar {
margin-left: -46px;
}
@@ -161,7 +165,24 @@
.branch-commit {
color: $gl-gray;
- .commit-id, .commit-row-message {
+
+ .commit-icon {
+ text-align: center;
+ display: inline-block;
+
+ svg {
+ height: 14px;
+ width: 14px;
+ vertical-align: middle;
+ fill: $table-text-gray;
+ }
+ }
+
+ .commit-id {
+ color: $gl-link-color;
+ }
+
+ .commit-row-message {
color: $gl-gray;
}
}
diff --git a/app/assets/stylesheets/pages/confirmation.scss b/app/assets/stylesheets/pages/confirmation.scss
index 292225c5261..81e5cee240d 100644
--- a/app/assets/stylesheets/pages/confirmation.scss
+++ b/app/assets/stylesheets/pages/confirmation.scss
@@ -2,7 +2,12 @@
margin-bottom: 20px;
border-bottom: 1px solid #eee;
- > h1, h2, h3, h4, h5, h6 {
+ > h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
font-weight: 400;
}
@@ -10,7 +15,8 @@
margin-bottom: 20px;
}
- ul, ol {
+ ul,
+ ol {
padding-left: 0;
}
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
new file mode 100644
index 00000000000..498a8f68e49
--- /dev/null
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -0,0 +1,482 @@
+#cycle-analytics {
+ max-width: 1000px;
+ margin: 24px auto 0;
+ position: relative;
+
+ .col-headers {
+ ul {
+ margin: 0;
+ padding: 0;
+ @include clearfix;
+ }
+
+ li {
+ display: inline-block;
+ float: left;
+ line-height: 50px;
+ width: 20%;
+ }
+
+
+ .fa {
+ color: $cycle-analytics-light-gray;
+ }
+
+ .stage-header {
+ width: 28%;
+ padding-left: $gl-padding;
+ }
+
+ .median-header {
+ width: 12%;
+ }
+
+ .event-header {
+ width: 45%;
+ padding-left: $gl-padding;
+ }
+
+ .total-time-header {
+ width: 15%;
+ text-align: right;
+ padding-right: $gl-padding;
+ }
+
+ .stage-name {
+ font-weight: 600;
+ }
+ }
+
+ .panel {
+ .content-block {
+ padding: 24px 0;
+ border-bottom: none;
+ position: relative;
+
+ @media (max-width: $screen-sm-min) {
+ padding: 6px 0 24px;
+ }
+ }
+
+ .column {
+ text-align: center;
+
+ @media (max-width: $screen-sm-min) {
+ padding: 15px 0;
+ }
+
+ .header {
+ font-size: 30px;
+ line-height: 38px;
+ font-weight: normal;
+ margin: 0;
+ }
+
+ .text {
+ color: $layout-link-gray;
+ margin: 0;
+ }
+
+ &:last-child {
+ @media (max-width: $screen-sm-min) {
+ text-align: center;
+ }
+ }
+ }
+ }
+
+ .js-ca-dropdown {
+ top: $gl-padding-top;
+ }
+
+ .bordered-box {
+ border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+ }
+
+ .content-list {
+ li {
+ padding: 18px $gl-padding $gl-padding;
+
+ .container-fluid {
+ padding: 0;
+ }
+ }
+
+ .title-col {
+ p {
+ margin: 0;
+
+ &.title {
+ line-height: 19px;
+ font-size: 15px;
+ font-weight: 600;
+ color: $gl-title-color;
+ }
+
+ &.text {
+ color: $layout-link-gray;
+
+ &.value-col {
+ color: $gl-title-color;
+ }
+ }
+ }
+ }
+
+ .value-col {
+ text-align: right;
+
+ span {
+ position: relative;
+ vertical-align: middle;
+ top: 3px;
+ }
+ }
+ }
+
+ .landing {
+ margin-bottom: $gl-padding;
+ overflow: hidden;
+
+ .dismiss-icon {
+ position: absolute;
+ right: $cycle-analytics-box-padding;
+ cursor: pointer;
+ color: #b2b2b2;
+ }
+
+ .svg-container {
+ text-align: center;
+
+ svg {
+ width: 136px;
+ height: 136px;
+ }
+ }
+
+ .inner-content {
+ @media (max-width: $screen-sm-min) {
+ padding: 0 28px;
+ text-align: center;
+ }
+
+ h4 {
+ color: $gl-text-color;
+ font-size: 17px;
+ }
+
+ p {
+ color: $cycle-analytics-box-text-color;
+ margin-bottom: $gl-padding;
+ }
+ }
+ }
+
+ .fa-spinner {
+ font-size: 28px;
+ position: relative;
+ margin-left: -20px;
+ left: 50%;
+ margin-top: 36px;
+ }
+
+ .stage-panel-body {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ .stage-nav,
+ .stage-entries {
+ display: flex;
+ vertical-align: top;
+ font-size: $gl-font-size;
+ }
+
+ .stage-nav {
+ width: 40%;
+ margin-bottom: 0;
+
+ ul {
+ padding: 0;
+ margin: 0;
+ width: 100%;
+ }
+
+ li {
+ list-style-type: none;
+ @include clearfix;
+ }
+
+ .stage-nav-item {
+ display: block;
+ line-height: 65px;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ border-right: 1px solid $border-color;
+ background-color: $gray-light;
+ cursor: default;
+
+ &.active {
+ background-color: transparent;
+ border-right-color: transparent;
+ border-top-color: $border-color;
+ border-bottom-color: $border-color;
+ box-shadow: inset 2px 0 0 0 $active-item-blue;
+
+ .stage-name {
+ font-weight: 600;
+ }
+ }
+
+ &:hover:not(.active) {
+ background-color: $gray-lightest;
+ box-shadow: inset 2px 0 0 0 $border-color;
+ }
+
+ &:first-child {
+ border-top: none;
+ }
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .stage-nav-item-cell {
+ float: left;
+
+ &.stage-name {
+ width: 70%;
+ }
+
+ &.stage-median {
+ width: 30%;
+ }
+ }
+
+ .stage-name {
+ padding-left: 16px;
+ }
+
+ .stage-empty,
+ .not-available {
+ color: $gl-text-color-light;
+ }
+ }
+ }
+
+ .stage-panel-container {
+ width: 100%;
+ overflow: auto;
+ }
+
+ .stage-panel {
+ min-width: 968px;
+
+ .panel-heading {
+ padding: 0;
+ background-color: transparent;
+ }
+
+ .events-description {
+ line-height: 65px;
+ padding-left: $gl-padding;
+ }
+ }
+
+ .stage-events {
+ width: 60%;
+ overflow: scroll;
+ height: 467px;
+ }
+
+ .stage-event-list {
+ margin: 0;
+ padding: 0;
+ }
+
+ .stage-event-item {
+ list-style-type: none;
+ padding: 0 0 $gl-padding;
+ margin: 0 $gl-padding $gl-padding;
+ border-bottom: 1px solid $gray-darker;
+ @include clearfix;
+
+ &:last-child {
+ border-bottom: none;
+ margin-bottom: 0;
+ }
+
+ .item-details,
+ .item-time {
+ float: left;
+ }
+
+ .item-details {
+ width: 75%;
+ }
+
+ .item-title {
+ margin: 0 0 2px;
+
+ &.issue-title,
+ &.commit-title,
+ &.merge-merquest-title {
+ max-width: 100%;
+ display: block;
+ @include text-overflow();
+
+ a {
+ color: $gl-dark-link-color;
+ }
+ }
+ }
+
+ .item-time {
+ width: 25%;
+ text-align: right;
+ }
+
+ .total-time {
+ font-size: $cycle-analytics-big-font;
+ color: $cycle-analytics-dark-text;
+
+ span {
+ color: $gl-text-color;
+ font-size: $gl-font-size;
+ }
+ }
+
+ .issue-date,
+ .build-date {
+ color: $gl-text-color;
+ }
+
+ .issue-link,
+ .commit-author-link,
+ .issue-author-link {
+ color: $gl-dark-link-color;
+ }
+
+ // Custom CSS for components
+ .item-conmmit-component {
+ .commit-icon {
+ position: relative;
+ top: 3px;
+ left: 1px;
+ display: inline-block;
+
+ svg {
+ float: left;
+ }
+ }
+ }
+
+ .merge-request-branch {
+ a {
+ max-width: 180px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ display: inline-block;
+ vertical-align: bottom;
+ }
+ }
+ }
+
+ // Custom Styles for stage items
+ .item-build-component {
+
+ .item-title {
+ .icon-build-status {
+ float: left;
+ margin-right: 5px;
+ position: relative;
+ top: 2px;
+ }
+
+ .item-build-name {
+ color: $gl-title-color;
+ }
+
+ .pipeline-id {
+ color: $gl-title-color;
+ padding: 0 3px 0 0;
+ }
+
+ .branch-name {
+ color: $black;
+ display: inline-block;
+ max-width: 180px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ line-height: 1.3;
+ vertical-align: top;
+ }
+
+ .short-sha {
+ color: $gl-link-color;
+ line-height: 1.3;
+ vertical-align: top;
+ font-weight: normal;
+ }
+
+ .fa {
+ color: $gl-text-color-light;
+ font-size: $code_font_size;
+ }
+ }
+ }
+
+ .empty-stage,
+ .no-access-stage {
+ text-align: center;
+ width: 75%;
+ margin: 0 auto;
+ padding-top: 130px;
+ color: $gl-text-color-light;
+
+ h4 {
+ color: $gl-text-color;
+ }
+ }
+
+ .empty-stage {
+ .icon-no-data {
+ height: 36px;
+ width: 78px;
+ display: inline-block;
+ margin-bottom: 20px;
+ }
+ }
+
+ .no-access-stage {
+ .icon-lock {
+ height: 36px;
+ width: 78px;
+ display: inline-block;
+ margin-bottom: 20px;
+ }
+ }
+}
+
+.cycle-analytics-overview {
+ padding-top: 100px;
+
+ .overview-details {
+ display: flex;
+ align-items: center;
+ }
+
+ .overview-image {
+ text-align: right;
+ }
+
+ .overview-icon {
+ svg {
+ width: 365px;
+ height: 227px;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss
index 42928ee279c..016bab104eb 100644
--- a/app/assets/stylesheets/pages/dashboard.scss
+++ b/app/assets/stylesheets/pages/dashboard.scss
@@ -5,6 +5,7 @@
background: $background-color;
border-top-left-radius: 0;
}
+
border-top-left-radius: 0;
}
}
@@ -17,6 +18,7 @@
float: left;
@extend .col-md-2;
}
+
.btn {
margin-left: 5px;
float: left;
@@ -34,10 +36,6 @@
}
}
-.dash-project-avatar {
- float: left;
-}
-
.dash-project-access-icon {
float: left;
margin-right: 5px;
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index 4d9c73c6840..0f0c0abe7ae 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -13,16 +13,19 @@
color: #5c5d5e;
}
- .issue_created_ago, .author_link {
+ .issue_created_ago,
+ .author_link {
white-space: nowrap;
}
}
.detail-page-description {
.title {
- margin: 0;
- font-size: 23px;
+ margin: 0 0 16px;
+ font-size: 2em;
color: $gl-gray-dark;
+ padding: 0 0 0.3em;
+ border-bottom: 1px solid $white-dark;
}
.description {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 21cee2e3a70..99fdea15218 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -33,13 +33,25 @@
font-size: smaller;
}
}
+
+ .file-title {
+ cursor: pointer;
+
+ &:hover {
+ background-color: $dark-background-color;
+ }
+
+ .diff-toggle-caret {
+ padding-right: 6px;
+ }
+ }
+
.diff-content {
overflow: auto;
overflow-y: hidden;
background: #fff;
color: #333;
border-radius: 0 0 3px 3px;
- -webkit-overflow-scrolling: auto;
.unfold {
cursor: pointer;
@@ -68,6 +80,11 @@
border-collapse: separate;
margin: 0;
padding: 0;
+ table-layout: fixed;
+
+ .diff-line-num {
+ width: 50px;
+ }
.line_holder td {
line-height: $code_line_height;
@@ -75,20 +92,6 @@
&.noteable_line {
position: relative;
-
- &.old {
- &:before {
- content: '-';
- position: absolute;
- }
- }
-
- &.new {
- &:before {
- content: '+';
- position: absolute;
- }
- }
}
span {
@@ -98,10 +101,6 @@
}
tr.line_holder.parallel {
- .old_line, .new_line {
- min-width: 50px;
- }
-
td.line_content.parallel {
width: 46%;
}
@@ -111,7 +110,8 @@
}
}
- .old_line, .new_line {
+ .old_line,
+ .new_line {
margin: 0;
padding: 0;
border: none;
@@ -122,20 +122,24 @@
max-width: 50px;
width: 35px;
@include user-select(none);
+
a {
float: left;
width: 35px;
font-weight: normal;
+
&:hover {
text-decoration: underline;
}
}
}
+
.line_content {
display: block;
margin: 0;
- padding: 0 0.5em;
+ padding: 0 1.5em;
border: none;
+ position: relative;
&.parallel {
display: table-cell;
@@ -144,16 +148,34 @@
word-break: break-all;
}
}
+
+ &.old {
+ &::before {
+ content: '-';
+ position: absolute;
+ left: 0.5em;
+ }
+ }
+
+ &.new {
+ &::before {
+ content: '+';
+ position: absolute;
+ left: 0.5em;
+ }
+ }
}
.text-file.diff-wrap-lines table .line_holder td span {
white-space: pre-wrap;
}
}
+
.image {
background: #ddd;
text-align: center;
padding: 30px;
+
.wrap {
display: inline-block;
}
@@ -162,6 +184,7 @@
display: inline-block;
background-color: #fff;
line-height: 0;
+
img {
border: 1px solid #fff;
background-image: linear-gradient(45deg, #e5e5e5 25%, transparent 25%, transparent 75%, #e5e5e5 75%, #e5e5e5 100%),
@@ -170,6 +193,7 @@
background-position: 0 0, 5px 5px;
max-width: 100%;
}
+
&.deleted {
border: 1px solid $deleted;
}
@@ -178,6 +202,7 @@
border: 1px solid $added;
}
}
+
.image-info {
font-size: 12px;
margin: 5px 0 0;
@@ -192,6 +217,7 @@
margin: auto;
position: relative;
}
+
.swipe-wrap {
overflow: hidden;
border-left: 1px solid #999;
@@ -200,10 +226,12 @@
top: 13px;
right: 7px;
}
+
.frame {
top: 0;
right: 0;
position: absolute;
+
&.deleted {
margin: 0;
display: block;
@@ -211,6 +239,7 @@
right: 7px;
}
}
+
.swipe-bar {
display: block;
height: 100%;
@@ -218,14 +247,17 @@
z-index: 100;
position: absolute;
cursor: pointer;
+
&:hover {
.top-handle {
background-position: -15px 3px;
}
+
.bottom-handle {
background-position: -15px -11px;
}
}
+
.top-handle {
display: block;
height: 14px;
@@ -234,6 +266,7 @@
top: 0;
background: image-url('swipemode_sprites.gif') 0 3px no-repeat;
}
+
.bottom-handle {
display: block;
height: 14px;
@@ -251,12 +284,15 @@
margin: auto;
position: relative;
}
- .frame.added, .frame.deleted {
+
+ .frame.added,
+ .frame.deleted {
position: absolute;
display: block;
top: 0;
left: 0;
}
+
.controls {
display: block;
height: 14px;
@@ -310,12 +346,14 @@
}
//.view.onion-skin
}
+
.view-modes {
padding: 10px;
text-align: center;
background: #eee;
- ul, li {
+ ul,
+ li {
list-style: none;
margin: 0;
padding: 0;
@@ -327,19 +365,24 @@
border-left: 1px solid #c1c1c1;
padding: 0 12px 0 16px;
cursor: pointer;
+
&:first-child {
border-left: none;
}
+
&:hover {
text-decoration: underline;
}
+
&.active {
&:hover {
text-decoration: none;
}
+
cursor: default;
color: #333;
}
+
&.disabled {
display: none;
}
@@ -431,7 +474,7 @@
.file-holder {
.diff-line-num:not(.js-unfold-bottom) {
a {
- &:before {
+ &::before {
content: attr(data-linenumber);
}
}
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 1aa4e06d975..778126bcfb7 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -1,7 +1,7 @@
.file-editor {
#editor {
border: none;
- @include border-radius(0);
+ border-radius: 0;
height: 500px;
margin: 0;
padding: 0;
@@ -15,6 +15,7 @@
.cancel-btn {
color: #b94a48;
+
&:hover {
color: #b94a48;
}
@@ -54,11 +55,16 @@
float: left;
}
+ .file-buttons {
+ font-size: 0;
+ }
+
.select2 {
float: right;
}
.encoding-selector,
+ .soft-wrap-toggle,
.license-selector,
.gitignore-selector,
.gitlab-ci-yml-selector {
@@ -67,7 +73,31 @@
font-family: $regular_font;
}
- .gitignore-selector, .license-selector, .gitlab-ci-yml-selector {
+ .soft-wrap-toggle {
+ margin: 0 $btn-side-margin;
+
+ .soft-wrap {
+ display: block;
+ }
+
+ .no-wrap {
+ display: none;
+ }
+
+ &.soft-wrap-active {
+ .soft-wrap {
+ display: none;
+ }
+
+ .no-wrap {
+ display: block;
+ }
+ }
+ }
+
+ .gitignore-selector,
+ .license-selector,
+ .gitlab-ci-yml-selector {
.dropdown {
line-height: 21px;
}
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index d01c60ee6ab..4b382e8adaf 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -1,14 +1,68 @@
+.deployments-container {
+ width: 100%;
+ overflow: auto;
+}
+
+.environments-list-loading {
+ width: 100%;
+ font-size: 34px;
+}
+
+@media (max-width: $screen-sm-min) {
+ .environments-container {
+ width: 100%;
+ overflow: auto;
+ }
+}
+
.environments {
+ table-layout: fixed;
+
+ .environments-commit,
+ .environments-actions,
+ .environments-deploy,
+ .environments-build,
+ .environments-date {
+ position: static;
+ float: none;
+ display: table-cell;
+ }
+
+ .environments-commit,
+ .environments-actions {
+ width: 20%;
+ }
+
+ .environments-deploy,
+ .environments-build,
+ .environments-date {
+ width: 10%;
+ }
+
+ .environments-name {
+ width: 30%;
+ }
+
+ .deployment-column {
+ .avatar {
+ float: none;
+ }
+ }
.commit-title {
margin: 0;
}
+ .avatar-image-container {
+ text-decoration: none;
+ }
+
.icon-play {
height: 13px;
width: 12px;
}
+ .external-url,
.dropdown-new {
color: $table-text-gray;
}
@@ -21,16 +75,63 @@
}
}
+ .build-link,
.branch-name {
color: $gl-dark-link-color;
}
-}
-.table.builds.environments {
- min-width: 500px;
+ .stop-env-link,
+ .external-url {
+ color: $table-text-gray;
+
+ .stop-env-icon {
+ font-size: 14px;
+ }
+ }
+
+ .deployment {
+ .build-column {
+
+ .build-link {
+ color: $gl-dark-link-color;
+ }
+
+ .avatar {
+ float: none;
+ }
+ }
+ }
+
+ .children-row .environment-name {
+ margin-left: 17px;
+ margin-right: -17px;
+ }
+ .folder-icon {
+ padding: 0 5px 0 0;
+ }
+
+ .folder-name {
+ cursor: pointer;
+
+ .badge {
+ font-weight: normal;
+ background-color: $gray-darker;
+ color: $gl-placeholder-color;
+ vertical-align: baseline;
+ }
+ }
+}
+
+.table.ci-table.environments {
.icon-container {
width: 20px;
text-align: center;
}
+
+ .branch-commit {
+ .commit-id {
+ margin-right: 0;
+ }
+ }
}
diff --git a/app/assets/stylesheets/pages/errors.scss b/app/assets/stylesheets/pages/errors.scss
index 32d2d7b1dbf..11309817d31 100644
--- a/app/assets/stylesheets/pages/errors.scss
+++ b/app/assets/stylesheets/pages/errors.scss
@@ -2,7 +2,9 @@
max-width: 400px;
margin: 0 auto;
- h1, h2, h3 {
+ h1,
+ h2,
+ h3 {
text-align: center;
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 1d00da1266c..3004959ff7b 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -78,6 +78,7 @@
margin-bottom: 0;
}
}
+
.event-note-icon {
color: #777;
float: left;
@@ -86,21 +87,23 @@
margin-right: 5px;
}
}
+
.event_icon {
position: relative;
float: right;
border: 1px solid #eee;
padding: 5px;
- @include border-radius(5px);
+ border-radius: 5px;
background: $gray-light;
margin-left: 10px;
top: -6px;
+
img {
width: 20px;
}
}
- &:last-child { border: none }
+ &:last-child { border: none; }
.event_commits {
li {
@@ -109,6 +112,7 @@
padding: 3px;
padding-left: 0;
border: none;
+
.commit-row-title {
font-size: $gl-font-size;
}
@@ -117,6 +121,7 @@
&.commits-stat {
display: block;
padding: 0 3px 0 0;
+
&:hover {
background: none;
}
@@ -137,7 +142,7 @@
.event-last-push {
overflow: auto;
width: 100%;
-
+
.event-last-push-text {
@include str-truncated(100%);
padding: 4px 0;
@@ -158,6 +163,7 @@
overflow: visible;
max-width: 100%;
}
+
.avatar {
display: none;
}
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index b657ca47d38..57d028cec8c 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -1,22 +1,9 @@
-.member-search-form {
- float: left;
-
- input[type='search'] {
- width: 225px;
- vertical-align: bottom;
-
- @media (max-width: $screen-xs-max) {
- width: 100px;
- vertical-align: bottom;
- }
- }
-}
-
.milestone-row {
@include str-truncated(90%);
}
.dashboard .side .panel .panel-heading .input-group {
+
.form-control {
height: 42px;
}
@@ -40,18 +27,55 @@
}
}
-.groups-cover-block {
- .container-fluid {
- position: relative;
+.group-buttons {
+ .notification-dropdown {
+ display: inline-block;
}
+}
+
+.groups-header {
+ @media (min-width: $screen-sm-min) {
+ .nav-links {
+ width: 35%;
+ }
+
+ .nav-controls {
+ width: 65%;
+ }
+ }
+}
+
+.groups-empty-state {
+ padding: 50px 100px;
+ overflow: hidden;
+
+ @media (max-width: $screen-md-min) {
+ padding: 50px 0;
+ }
+
+ svg {
+ float: right;
+
+ @media (max-width: $screen-md-min) {
+ float: none;
+ display: block;
+ width: 250px;
+ position: relative;
+ left: 50%;
+ margin-left: -125px;
+ }
+ }
+
+ .text-content {
+ float: left;
+ width: 460px;
+ margin-top: 120px;
- .group-right-buttons {
- position: absolute;
- right: 16px;
- .btn {
- @include btn-gray;
- padding: 3px 10px;
- background-color: $background-color;
+ @media (max-width: $screen-md-min) {
+ float: none;
+ margin-top: 60px;
+ width: auto;
+ text-align: center;
}
}
}
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index 00ab42bec5c..a48b4c65db8 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -23,28 +23,28 @@
color: #555;
tbody:first-child tr:first-child {
- padding-top: 0
+ padding-top: 0;
}
th {
padding-top: 15px;
line-height: 1.5;
color: #333;
- text-align: left
+ text-align: left;
}
td {
padding-top: 3px;
padding-bottom: 3px;
vertical-align: top;
- line-height: 20px
+ line-height: 20px;
}
.shortcut {
padding-right: 10px;
color: #999;
text-align: right;
- white-space: nowrap
+ white-space: nowrap;
}
.key {
diff --git a/app/assets/stylesheets/pages/icons.scss b/app/assets/stylesheets/pages/icons.scss
new file mode 100644
index 00000000000..226bd2ead31
--- /dev/null
+++ b/app/assets/stylesheets/pages/icons.scss
@@ -0,0 +1,51 @@
+.ci-status-icon-success {
+ color: $gl-success;
+
+ svg {
+ fill: $gl-success;
+ }
+}
+
+.ci-status-icon-failed {
+ color: $gl-danger;
+
+ svg {
+ fill: $gl-danger;
+ }
+}
+
+.ci-status-icon-pending,
+.ci-status-icon-success_with_warnings {
+ color: $gl-warning;
+
+ svg {
+ fill: $gl-warning;
+ }
+}
+
+.ci-status-icon-running {
+ color: $blue-normal;
+
+ svg {
+ fill: $blue-normal;
+ }
+}
+
+.ci-status-icon-canceled,
+.ci-status-icon-disabled,
+.ci-status-icon-not-found {
+ color: $gl-gray;
+
+ svg {
+ fill: $gl-gray;
+ }
+}
+
+.ci-status-icon-created,
+.ci-status-icon-skipped {
+ color: $gray-darkest;
+
+ svg {
+ fill: $gray-darkest;
+ }
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 41079b6eeb5..773155fe80a 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -27,6 +27,7 @@
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
+
.color-label {
padding: 6px 10px;
}
@@ -128,7 +129,7 @@
}
.selectbox {
- display: none
+ display: none;
}
.btn-clipboard {
@@ -199,7 +200,7 @@
display: none;
/* Small devices (tablets, 768px and up) */
@media (min-width: $screen-sm-min) {
- display: block
+ display: block;
}
width: $sidebar_collapsed_width;
@@ -266,20 +267,6 @@
}
}
- .issuable-header-btn {
- background: $gray-normal;
- border: 1px solid $border-gray-normal;
-
- &:hover {
- background: $gray-dark;
- border: 1px solid $border-gray-dark;
- }
-
- &.btn-primary {
- @extend .btn-primary
- }
- }
-
a {
&:hover {
color: $md-link-color;
@@ -400,6 +387,7 @@
.js-issuable-selector {
width: 100%;
}
+
@media (max-width: $screen-sm-max) {
margin-bottom: $gl-padding;
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 3ac34cbc829..eb171195309 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -37,12 +37,14 @@ ul.related-merge-requests > li {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
+
.merge-request-id {
flex-shrink: 0;
}
}
-.merge-requests-title, .related-branches-title {
+.merge-requests-title,
+.related-branches-title {
font-size: 16px;
font-weight: 600;
}
@@ -53,9 +55,28 @@ ul.related-merge-requests > li {
}
.merge-request-status {
- color: $gl-gray;
- font-size: 15px;
- font-weight: bold;
+ font-size: 13px;
+ padding: 0 5px;
+ color: $white-light;
+ height: 20px;
+ border-radius: 3px;
+ line-height: 18px;
+ border: 1px solid;
+
+ &.merged {
+ border-color: darken($blue-normal, 10%);
+ background: $blue-normal;
+ }
+
+ &.closed {
+ border-color: darken($red-normal, 10%);
+ background: $red-normal;
+ }
+
+ &.open {
+ border: 1px solid darken($green-normal, 10%);
+ background: $green-normal;
+ }
}
.merge-request,
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 38c7cd98e41..e39ce19f846 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -1,7 +1,8 @@
.suggest-colors {
margin-top: 5px;
+
a {
- @include border-radius(4px);
+ border-radius: 4px;
width: 30px;
height: 30px;
display: inline-block;
@@ -17,7 +18,7 @@
overflow: hidden;
a {
- @include border-radius(0);
+ border-radius: 0;
width: (100% / 7);
margin-right: 0;
margin-bottom: -5px;
@@ -59,6 +60,27 @@
width: 200px;
margin-bottom: 0;
}
+
+ .label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: middle;
+ max-width: 100%;
+ }
+ }
+
+ .label-type {
+ display: block;
+ margin-bottom: 10px;
+ margin-left: 50px;
+
+ @media (min-width: $screen-sm-min) {
+ display: inline-block;
+ width: 100px;
+ margin-left: 10px;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
}
.label-description {
@@ -68,7 +90,7 @@
@media (min-width: $screen-sm-min) {
display: inline-block;
- width: 40%;
+ width: 30%;
margin-left: 10px;
margin-bottom: 0;
vertical-align: middle;
@@ -200,7 +222,22 @@
width: 100%;
}
+.label-subscription {
+ vertical-align: middle;
+
+ .dropdown-group-label a {
+ cursor: pointer;
+ }
+}
+
.label-subscribe-button {
+ .label-subscribe-button-icon {
+ &[disabled] {
+ opacity: 0.5;
+ pointer-events: none;
+ }
+ }
+
.label-subscribe-button-loading {
display: none;
}
diff --git a/app/assets/stylesheets/pages/lint.scss b/app/assets/stylesheets/pages/lint.scss
index 6926448519e..8290519dc25 100644
--- a/app/assets/stylesheets/pages/lint.scss
+++ b/app/assets/stylesheets/pages/lint.scss
@@ -3,6 +3,7 @@
font-size: 19px;
color: red;
}
+
.correct-syntax {
font-size: 19px;
color: #47a447;
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 403171d4532..54c89d75e94 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -17,6 +17,7 @@
line-height: 1.5;
p {
+ font-size: 18px;
color: #888;
}
@@ -36,10 +37,15 @@
}
}
- .login-box {
- background: #fafafa;
- border-radius: 10px;
- box-shadow: 0 0 2px #ccc;
+ p {
+ font-size: 13px;
+ }
+
+ .login-box,
+ .omniauth-container {
+ box-shadow: 0 0 0 1px $border-color;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 2px;
padding: 15px;
.login-heading h3 {
@@ -58,42 +64,133 @@
a.forgot {
float: right;
- padding-top: 6px
+ padding-top: 6px;
}
.nav .active a {
background: transparent;
}
+
+ // Styles the glowing border of focused input for username async validation
+ .login-body {
+ font-size: 13px;
+
+ input + p {
+ margin-top: 5px;
+ }
+
+ .username .validation-success {
+ color: $green-normal;
+ }
+
+ .username .validation-error {
+ color: $red-normal;
+ }
+ }
}
- .form-control {
- font-size: 14px;
- padding: 10px 8px;
- width: 100%;
- height: auto;
+ .omniauth-container {
+ p {
+ margin: 0;
+ }
+ }
+
+ .new-session-tabs {
+ display: -webkit-flex;
+ display: flex;
+ box-shadow: 0 0 0 1px $border-color;
+ border-top-right-radius: $border-radius-default;
+ border-top-left-radius: $border-radius-default;
+
+ li {
+ flex: 1;
+ text-align: center;
- &.top {
- @include border-radius(5px 5px 0 0);
- margin-bottom: 0;
+ &:first-of-type {
+ border-top-left-radius: $border-radius-default;
+ }
+
+ &:last-of-type {
+ border-left: 1px solid $border-color;
+ border-top-right-radius: $border-radius-default;
+ }
+
+ &:not(.active) {
+ background-color: $gray-light;
+ border-left: 1px solid $border-color;
+ }
+
+ a {
+ width: 100%;
+ font-size: 18px;
+
+ &:hover {
+ border: 1px solid transparent;
+ }
+ }
+
+ &.active {
+ border-bottom: 1px solid $border-color;
+
+ a {
+ border: none;
+ border-bottom: 2px solid $link-underline-blue;
+ color: $black;
+
+ &:hover {
+ border-bottom: 2px solid $link-underline-blue;
+ }
+ }
+ }
}
+ }
+
+ // Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long).
+ // These styles prevent this from breaking the layout, and only applied when providers are configured.
+
+ .new-session-tabs.custom-provider-tabs {
+ flex-wrap: wrap;
- &.bottom {
- @include border-radius(0 0 5px 5px);
- border-top: 0;
- margin-bottom: 20px;
+ li {
+ min-width: 85px;
+ flex-basis: auto;
+
+ // This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen.
+ // We are making somewhat of an assumption about the configuration here: that users do not have more than
+ // 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any
+ // of them. If either condition is false, this will work as expected. If both are true, there may be a missing border
+ // above one of the bottom row elements. If you know a better way, please implement it!
+ &:nth-child(n+5) {
+ border-top: 1px solid $border-color;
+ }
}
- &.middle {
- border-top: 0;
- margin-bottom: 0;
- @include border-radius(0);
+ a {
+ font-size: 16px;
}
+ }
- &:active, &:focus {
+
+ .form-control {
+ &:active,
+ &:focus {
background-color: #fff;
}
}
+ label {
+ font-weight: normal;
+ }
+
+ .submit-container {
+ margin-top: 16px;
+ }
+
+ input[type="submit"] {
+ @extend .btn-block;
+ margin-bottom: 0;
+ }
+
.devise-errors {
h2 {
margin-top: 0;
@@ -101,20 +198,13 @@
color: #a00;
}
}
-
- .remember-me {
- margin-top: -10px;
-
- label {
- font-weight: normal;
- }
- }
}
@media (max-width: $screen-xs-max) {
.login-page {
.col-sm-5.pull-right {
float: none !important;
+ margin-bottom: 45px;
}
}
}
@@ -127,3 +217,41 @@
height: 32px;
}
}
+
+.devise-layout-html {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+}
+
+// Fixes footer container to bottom of viewport
+.devise-layout-html body {
+ // offset height of fixed header + 1 to avoid scroll
+ height: calc(100% - 51px);
+ margin: 0;
+ padding: 0;
+
+ .page-wrap {
+ min-height: 100%;
+ position: relative;
+ }
+
+ .footer-container,
+ hr.footer-fixed {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 40px;
+ background: $white-light;
+ }
+
+ .navless-container {
+ padding: 65px 15px; // height of footer + bottom padding of email confirmation link
+
+ @media (max-width: $screen-xs-max) {
+ padding: 0 15px 65px;
+ }
+ }
+}
+
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
new file mode 100644
index 00000000000..756efa9c7fa
--- /dev/null
+++ b/app/assets/stylesheets/pages/members.scss
@@ -0,0 +1,98 @@
+.project-members-title {
+ padding-bottom: 10px;
+ border-bottom: 1px solid $border-color;
+}
+
+.member {
+ .list-item-name {
+ @media (min-width: $screen-sm-min) {
+ float: left;
+ width: 50%;
+ }
+
+ strong {
+ font-weight: 600;
+ }
+ }
+
+ .controls {
+ @media (min-width: $screen-sm-min) {
+ display: -webkit-flex;
+ display: flex;
+ width: 400px;
+ max-width: 50%;
+ }
+ }
+
+ .form-horizontal {
+ margin-top: 5px;
+
+ @media (min-width: $screen-sm-min) {
+ display: -webkit-flex;
+ display: flex;
+ width: 100%;
+ margin-top: 3px;
+ }
+ }
+
+ .btn-remove {
+ width: 100%;
+
+ @media (min-width: $screen-sm-min) {
+ width: auto;
+ }
+ }
+}
+
+.member-form-control {
+ @media (max-width: $screen-xs-max) {
+ padding: 5px 0;
+ margin-left: 0;
+ margin-right: 0;
+ }
+
+ @media (min-width: $screen-sm-min) {
+ width: 50%;
+ }
+}
+
+.member-access-text {
+ margin-left: auto;
+ line-height: 43px;
+}
+
+.member.existing-title {
+ @media (min-width: $screen-sm-min) {
+ float: left;
+ }
+}
+
+.member-search-form {
+ position: relative;
+
+ @media (min-width: $screen-sm-min) {
+ float: right;
+ }
+
+ .form-control {
+ width: 100%;
+ padding-right: 35px;
+
+ @media (min-width: $screen-sm-min) {
+ width: 350px;
+ }
+ }
+}
+
+.member-search-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ height: 35px;
+ padding-left: 10px;
+ padding-right: 10px;
+ color: $gray-darkest;
+ background: transparent;
+ border: 0;
+ outline: 0;
+}
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index 5ec660799e3..19ab198c2e7 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -101,7 +101,8 @@ $colors: (
@mixin color-scheme($color) {
- .header.line_content, .diff-line-num {
+ .header.line_content,
+ .diff-line-num {
&.origin {
background-color: map-get($colors, #{$color}_header_origin_neutral);
border-color: map-get($colors, #{$color}_header_origin_neutral);
@@ -131,6 +132,7 @@ $colors: (
}
}
}
+
&.head {
background-color: map-get($colors, #{$color}_header_head_neutral);
border-color: map-get($colors, #{$color}_header_head_neutral);
@@ -174,6 +176,7 @@ $colors: (
background-color: map-get($colors, #{$color}_line_not_chosen);
}
}
+
&.head {
background-color: map-get($colors, #{$color}_line_head_neutral);
@@ -225,7 +228,6 @@ $colors: (
position: absolute;
right: 10px;
padding: 0;
- outline: none;
color: #fff;
width: 75px; // static width to make 2 buttons have same width
height: 19px;
@@ -235,4 +237,51 @@ $colors: (
.btn-success .fa-spinner {
color: #fff;
}
+
+ .editor-wrap {
+ &.is-loading {
+ .editor {
+ display: none;
+ }
+
+ .loading {
+ display: block;
+ }
+ }
+
+ &.saved {
+ .editor {
+ border-top: solid 2px $border-green-extra-light;
+ }
+ }
+
+ .editor {
+ pre {
+ height: 350px;
+ border: none;
+ border-radius: 0;
+ margin-bottom: 0;
+ }
+ }
+
+ .loading {
+ display: none;
+ }
+ }
+
+ .discard-changes-alert {
+ background-color: $background-color;
+ text-align: right;
+ padding: $gl-padding-top $gl-padding;
+ color: $gl-text-color;
+
+ .discard-actions {
+ display: inline-block;
+ margin-left: 10px;
+ }
+ }
+
+ .resolve-conflicts-form {
+ padding-top: $gl-padding;
+ }
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 96c06086867..da1187af41c 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -6,10 +6,11 @@
background: $background-color;
color: $gl-gray;
border: 1px solid $border-color;
- @include border-radius(2px);
+ border-radius: 2px;
form {
margin-bottom: 0;
+
.clearfix {
margin-bottom: 0;
}
@@ -23,7 +24,7 @@
.accept_merge_request {
&.ci-pending,
&.ci-running {
- @include btn-orange;
+ @include btn-blue;
}
&.ci-skipped,
@@ -46,6 +47,8 @@
&.right {
float: right;
+ padding-right: 0;
+
a {
color: $gl-gray;
}
@@ -58,7 +61,8 @@
}
.ci_widget {
- border-bottom: 1px solid #eef0f2;
+ border-bottom: 1px solid $well-inner-border;
+ color: $gl-gray;
svg {
margin-right: 4px;
@@ -67,47 +71,12 @@
overflow: visible;
}
- &.ci-success {
- color: $gl-success;
-
- a.environment {
- color: inherit;
- }
- }
-
&.ci-success_with_warnings {
- color: $gl-success;
i {
color: $gl-warning;
}
}
-
- &.ci-skipped {
- background-color: #eee;
- color: #888;
- }
-
- &.ci-pending {
- color: $gl-warning;
- }
-
- &.ci-running {
- color: $blue-normal;
- }
-
- &.ci-failed,
- &.ci-error {
- color: $gl-danger;
- }
-
- &.ci-canceled {
- color: $gl-gray;
- }
-
- a.monospace {
- color: inherit;
- }
}
.mr-widget-body,
@@ -120,6 +89,10 @@
color: #5c5d5e;
}
+ .js-deployment-link {
+ display: inline-block;
+ }
+
.mr-widget-body {
h4 {
font-weight: 600;
@@ -176,6 +149,15 @@
.ci-coverage {
float: right;
}
+
+ .stop-env-container {
+ color: $gl-text-color;
+ float: right;
+
+ a {
+ color: $gl-text-color;
+ }
+ }
}
.mr_source_commit,
@@ -187,6 +169,7 @@
padding-top: 2px;
padding-bottom: 2px;
list-style: none;
+
&:hover {
background: none;
}
@@ -203,6 +186,19 @@
word-break: break-all;
}
+.commits-empty {
+ text-align: center;
+
+ h4 {
+ padding-top: 20px;
+ padding-bottom: 10px;
+ }
+
+ svg {
+ width: 230px;
+ }
+}
+
.mr-list {
.merge-request {
padding: 10px 15px;
@@ -249,6 +245,10 @@
#modal_merge_info .modal-dialog {
width: 600px;
+ .dark {
+ margin-right: 40px;
+ }
+
.btn-clipboard {
@extend .pull-right;
@@ -263,12 +263,6 @@
line-height: 31px;
}
-.builds {
- .table-holder {
- overflow-x: auto;
- }
-}
-
.panel-new-merge-request {
.panel-heading {
padding: 5px 10px;
@@ -349,10 +343,14 @@
.issuable-form-select-holder {
display: inline-block;
width: 250px;
+
+ .dropdown-menu-toggle {
+ width: 100%;
+ }
}
.table-holder {
- .builds {
+ .ci-table {
th {
background-color: $white-light;
@@ -373,17 +371,55 @@
.mr-version-controls {
background: $background-color;
- padding: $gl-btn-padding;
- color: $gl-placeholder-color;
+ border-bottom: 1px solid $border-color;
+ color: $gl-text-color;
+
+ .mr-version-menus-container {
+ display: -webkit-flex;
+ display: flex;
+ -webkit-align-items: center;
+ align-items: center;
+ padding: 16px;
+ }
+
+ .content-block {
+ border-top: 1px solid $border-color;
+ padding: $gl-padding-top $gl-padding;
+ }
+
+ .comments-disabled-notif {
+ .btn {
+ margin-left: 5px;
+ }
+ }
+
+ .mr-version-dropdown,
+ .mr-version-compare-dropdown {
+ margin: 0 7px;
+ }
- a.btn-link {
- color: $gl-dark-link-color;
+ .dropdown-title {
+ color: $gl-text-color;
+ }
+
+ .fa-info-circle {
+ color: $orange-normal;
+ padding-right: 5px;
}
}
-.merge-request-details {
+.merge-request-tabs-holder {
+ background-color: $white-light;
+
+ &.affix {
+ top: 100px;
+ left: 0;
+ z-index: 10;
+ transition: right .15s;
+ }
- .title {
- margin-bottom: 20px;
+ &:not(.affix) .container-fluid {
+ padding-left: 0;
+ padding-right: 0;
}
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index b94f524b513..8843d1463db 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -2,13 +2,18 @@
max-width: 90%;
}
-li.milestone {
- h4 {
- font-weight: bold;
- }
+.milestones {
+ .milestone {
+ padding: 10px 16px;
- .progress {
- height: 6px;
+ h4 {
+ font-weight: bold;
+ }
+
+ .progress {
+ width: 100%;
+ height: 6px;
+ }
}
}
@@ -26,26 +31,78 @@ li.milestone {
margin-right: 7px;
}
- // Issue title
span a {
color: $gl-text-color;
+ word-wrap: break-word;
}
}
}
.milestone-summary {
- margin-bottom: 25px;
-
.milestone-stat {
+ white-space: nowrap;
margin-right: 10px;
+
+ &.with-drilldown {
+ margin-right: 2px;
+ }
}
.remaining-days {
color: $orange-light;
}
+
+ .milestone-stats-and-buttons {
+ display: flex;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+
+ @media (min-width: $screen-xs-min) {
+ justify-content: space-between;
+ flex-wrap: nowrap;
+ }
+ }
+
+ .milestone-progress-buttons {
+ order: 1;
+ margin-top: 10px;
+
+ @media (min-width: $screen-xs-min) {
+ order: 2;
+ margin-top: 0;
+ flex-shrink: 0;
+ }
+
+ .btn {
+ float: left;
+ margin-right: $btn-side-margin;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+ }
+
+ .milestone-stats {
+ order: 2;
+ width: 100%;
+ padding: 7px 0;
+ flex-shrink: 1;
+
+ @media (min-width: $screen-xs-min) {
+ // when displayed on one line stats go first, buttons second
+ order: 1;
+ }
+ }
+
+ .progress {
+ width: 100%;
+ margin: 15px 0;
+ }
}
-.issues-sortable-list, .merge_requests-sortable-list {
+.issues-sortable-list,
+.merge_requests-sortable-list {
.issuable-detail {
display: block;
margin-top: 7px;
@@ -54,6 +111,7 @@ li.milestone {
color: $gl-placeholder-color;
margin-right: 5px;
}
+
.avatar {
float: none;
}
@@ -64,3 +122,61 @@ li.milestone {
border-bottom: 1px solid $border-color;
padding: 20px 0;
}
+
+@media (max-width: $screen-sm-min) {
+ .milestone-actions {
+ @include clearfix();
+ padding-top: $gl-vert-padding;
+
+ .btn:first-child {
+ margin-left: 0;
+ }
+ }
+}
+
+.milestone-page-header {
+ display: flex;
+ flex-flow: row;
+ align-items: center;
+ flex-wrap: wrap;
+
+ .status-box {
+ margin-top: 0;
+ }
+
+ .milestone-buttons {
+ margin-left: auto;
+ }
+
+ .status-box {
+ order: 1;
+ }
+
+ .milestone-buttons {
+ order: 2;
+ }
+
+ .header-text-content {
+ order: 3;
+ width: 100%;
+ }
+
+ .milestone-buttons .verbose {
+ display: none;
+ }
+
+ @media (min-width: $screen-xs-min) {
+ .milestone-buttons .verbose {
+ display: inline;
+ }
+
+ .header-text-content {
+ order: 2;
+ width: auto;
+ }
+
+ .milestone-buttons {
+ order: 3;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index bd875b9823f..16ddef481bd 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -8,9 +8,10 @@
.diff-file .diff-content {
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
- filter: alpha(opacity=100);
+ filter: alpha(opacity = 100);
}
}
+
.diff-file,
.discussion {
.new-note {
@@ -23,7 +24,8 @@
display: none;
}
-.new-note, .note-edit-form {
+.new-note,
+.note-edit-form {
.note-form-actions {
margin-top: $gl-padding;
}
@@ -194,6 +196,7 @@
min-height: 140px;
max-height: 500px;
}
+
.note-form-actions {
background: transparent;
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 54124a3d658..e66c1f8d072 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -28,17 +28,121 @@ ul.notes {
}
}
- .note-created-ago, .note-updated-at {
+ .note-created-ago,
+ .note-updated-at {
white-space: nowrap;
}
.system-note {
font-size: 14px;
- padding-top: 10px;
- padding-bottom: 10px;
- background: #fdfdfd;
+ padding: 0;
+ clear: both;
+
+ &.timeline-entry::after {
+ clear: none;
+ }
+
+ .system-note-message {
+ display: inline-block;
+
+ &::first-letter {
+ text-transform: lowercase;
+ }
+
+ a {
+ color: $gl-link-color;
+ text-decoration: none;
+ }
+
+ p {
+ display: inline-block;
+ margin: 0;
+
+ &::first-letter {
+ text-transform: lowercase;
+ }
+ }
+ }
+
+ .timeline-content {
+ padding: 14px 10px;
+ }
+
+ .note-body {
+ overflow: hidden;
+
+ .system-note-commit-list-toggler {
+ display: none;
+ padding: 10px 0 0;
+ cursor: pointer;
+ position: relative;
+ z-index: 2;
+
+ &:hover {
+ color: $gl-link-color;
+ text-decoration: underline;
+ }
+ }
+
+ .note-text {
+ & p:first-child {
+ display: none;
+ }
+
+ &.system-note-commit-list {
+ max-height: 63px;
+ overflow: hidden;
+ display: block;
+
+ ul {
+ margin: 3px 0 3px 15px !important;
+
+ li {
+ font-family: $monospace_font;
+ font-size: 12px;
+ }
+ }
+
+ p:first-child {
+ display: none;
+ }
+
+ p:last-child {
+ a {
+ color: $gl-text-color;
+
+ &:hover {
+ color: $gl-link-color;
+ }
+ }
+ }
+
+ &::after {
+ content: '';
+ width: 100%;
+ height: 67px;
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ background: linear-gradient(rgba($gray-light, 0.1) -100px, $white-light 100%);
+ }
+
+ &.hide-shade {
+ max-height: 100%;
+ overflow: auto;
+
+ &::after {
+ display: none;
+ background: transparent;
+ }
+ }
+ }
+ }
+ }
.timeline-icon {
+ display: none;
+
.avatar {
visibility: hidden;
@@ -64,6 +168,12 @@ ul.notes {
position: relative;
border-bottom: 1px solid $table-border-gray;
+ &.note-discussion {
+ &.timeline-entry {
+ padding: 14px 10px;
+ }
+ }
+
&.is-editting {
.note-header,
.note-text,
@@ -87,10 +197,8 @@ ul.notes {
overflow: auto;
word-wrap: break-word;
@include md-typography;
-
// Reset ul style types since we're nested inside a ul already
@include bulleted-list;
-
ul.task-list {
ul:not(.task-list) {
padding-left: 1.3em;
@@ -104,11 +212,6 @@ ul.notes {
padding: 2px;
margin-top: 10px;
}
-
- .award-control {
- font-size: 13px;
- padding: 2px 5px;
- }
}
.note-header {
@@ -145,11 +248,36 @@ ul.notes {
}
}
+.page-sidebar-pinned.right-sidebar-expanded {
+ @media (max-width: $screen-lg-min) {
+ .note-header {
+ .note-headline-light {
+ display: block;
+ }
+
+ .note-actions {
+ position: absolute;
+ right: 0;
+ top: 0;
+ }
+ }
+ }
+}
+
// Diff code in discussion view
.discussion-body .diff-file {
+ .file-title {
+ cursor: default;
+
+ &:hover {
+ background-color: $gray-light;
+ }
+ }
+
.diff-header > span {
margin-right: 10px;
}
+
.line_content {
white-space: pre-wrap;
}
@@ -217,6 +345,10 @@ ul.notes {
}
}
+.discussion-header {
+ font-size: 14px;
+}
+
.note-headline-light,
.discussion-headline-light {
color: $notes-light-color;
@@ -334,7 +466,7 @@ ul.notes {
.add-diff-note {
margin-top: -4px;
- @include border-radius(40px);
+ border-radius: 40px;
background: #fff;
padding: 4px;
font-size: 16px;
@@ -345,6 +477,7 @@ ul.notes {
width: 32px;
// "hide" it by default
display: none;
+
&:hover {
background: $gl-info;
color: #fff;
@@ -448,7 +581,7 @@ ul.notes {
.discussion-next-btn {
svg {
margin: 0;
-
+
path {
fill: $gray-darkest;
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 1b4d12d3053..0027d2caf22 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -20,8 +20,20 @@
margin: 4px;
}
- .table.builds {
+ .table.ci-table {
min-width: 1200px;
+
+ .pipeline-id {
+ color: $black;
+ }
+
+ .branch-commit {
+ width: 30%;
+
+ .branch-name {
+ max-width: 195px;
+ }
+ }
}
}
@@ -39,13 +51,20 @@
overflow: auto;
}
-.table.builds {
+.table.ci-table {
min-width: 900px;
&.pipeline {
min-width: 650px;
}
+ &.builds-page {
+
+ tr {
+ height: 71px;
+ }
+ }
+
tr {
th {
padding: 16px 8px;
@@ -61,13 +80,14 @@
border-top-width: 1px;
}
- .commit-link {
-
- .ci-status {
+ .build.retried {
+ background-color: $gray-lightest;
+ }
- svg {
- top: 1px;
- margin-right: 0;
+ .commit-link {
+ a {
+ &:focus {
+ text-decoration: none;
}
}
@@ -76,6 +96,11 @@
}
}
+ .avatar {
+ margin-left: 0;
+ float: none;
+ }
+
.branch-commit {
.branch-name {
@@ -97,12 +122,11 @@
.fa {
font-size: 12px;
- color: $table-text-gray;
+ color: $gl-text-color;
}
.commit-id {
color: $gl-link-color;
- margin-right: 8px;
}
.commit-title {
@@ -113,10 +137,6 @@
text-overflow: ellipsis;
}
- .avatar {
- margin-left: 0;
- }
-
.label {
margin-right: 4px;
}
@@ -132,17 +152,11 @@
.icon-container {
display: inline-block;
- text-align: right;
- width: 15px;
+ width: 10px;
- .fa {
- position: relative;
- right: 3px;
- }
-
- svg {
- position: relative;
- right: 1px;
+ &.commit-icon {
+ width: 15px;
+ text-align: center;
}
}
@@ -171,12 +185,16 @@
&::after {
content: '';
width: 8px;
- position: absolute;;
+ position: absolute;
right: -7px;
- bottom: 8px;
+ bottom: 9px;
border-bottom: 2px solid $border-color;
}
}
+
+ a {
+ display: block;
+ }
}
}
@@ -220,9 +238,13 @@
.fa {
color: $table-text-gray;
- margin-right: 6px;
font-size: 14px;
}
+
+ svg,
+ .fa {
+ margin-right: 0;
+ }
}
.btn-remove {
@@ -263,23 +285,15 @@
.toggle-pipeline-btn {
background-color: $gray-dark;
- .caret {
- border-top: none;
- border-bottom: 4px solid;
- }
-
&.graph-collapsed {
background-color: $white-light;
-
- .caret {
- border-bottom: none;
- border-top: 4px solid;
- }
}
}
.pipeline-graph {
width: 100%;
+ background-color: $background-color;
+ padding: $gl-padding;
overflow: auto;
white-space: nowrap;
transition: max-height 0.3s, padding 0.3s;
@@ -301,16 +315,41 @@
.stage-column {
display: inline-block;
vertical-align: top;
- margin-right: 65px;
+
+ &:not(:last-child) {
+ margin-right: 44px;
+ }
+
+ &.left-margin {
+ &:not(:first-child) {
+ margin-left: 44px;
+
+ .left-connector {
+ &::before {
+ content: '';
+ position: absolute;
+ top: 48%;
+ left: -48px;
+ border-top: 2px solid $border-color;
+ width: 48px;
+ height: 1px;
+ }
+ }
+ }
+ }
+
+ &.no-margin {
+ margin: 0;
+ }
li {
list-style: none;
}
.stage-name {
- margin-bottom: 15px;
+ margin: 0 0 15px 10px;
font-weight: bold;
- width: 150px;
+ width: 176px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -318,18 +357,22 @@
.build {
border: 1px solid $border-color;
+ background-color: $white-light;
position: relative;
- padding: 6px 10px;
+ padding: 7px 10px 8px;
border-radius: 30px;
- width: 150px;
+ width: 186px;
margin-bottom: 10px;
+ &:hover {
+ background-color: $gray-lighter;
+ }
+
&.playable {
- background-color: $gray-light;
svg {
- height: 12px;
- width: 12px;
+ height: 13px;
+ width: 20px;
position: relative;
top: 1px;
@@ -340,10 +383,29 @@
}
.build-content {
- width: 130px;
+ display: -ms-flexbox;
+ display: -webkit-flex;
+ display: flex;
+ width: 164px;
+
+ .ci-status-icon {
+ svg {
+ height: 20px;
+ width: 20px;
+ }
+ }
+
+ .tooltip {
+ white-space: nowrap;
+
+ .tooltip-inner {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
.ci-status-text {
- width: 110px;
+ width: 135px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -354,47 +416,70 @@
}
a {
- color: $layout-link-gray;
+ color: $gl-text-color-light;
text-decoration: none;
-
- &:hover {
- .ci-status-text {
- text-decoration: underline;
- }
- }
}
.dropdown-menu-toggle {
+ background-color: transparent;
border: none;
width: auto;
padding: 0;
- color: $layout-link-gray;
+ color: $gl-text-color-light;
+ flex-grow: 1;
.ci-status-text {
- width: 80px;
+ max-width: 112px;
+ width: auto;
}
}
.grouped-pipeline-dropdown {
- padding: 8px 0;
- width: 200px;
+ padding: 0;
+ width: 186px;
left: auto;
- right: -214px;
+ right: -197px;
top: -9px;
- a:hover {
- .ci-status-text {
- text-decoration: none;
+ ul {
+ max-height: 245px;
+ overflow: auto;
+
+ li:first-child {
+ padding-top: 8px;
+ }
+
+ li:last-child {
+ padding-bottom: 8px;
}
}
+ a {
+ color: $gl-text-color;
+ padding: 7px 8px 8px;
+
+ &:hover {
+ background-color: $blue-light-transparent;
+ border-radius: 3px;
+
+ .ci-status-text {
+ text-decoration: none;
+ }
+ }
+ }
+
+ svg {
+ width: 14px;
+ height: 14px;
+ }
+
.ci-status-text {
- width: 145px;
+ width: 112px;
}
.arrow {
- &:before,
- &:after {
+ &::before,
+ &::after {
content: '';
display: inline-block;
position: absolute;
@@ -405,14 +490,14 @@
top: 18px;
}
- &:before {
+ &::before {
left: -5px;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: $border-color;
}
- &:after {
+ &::after {
left: -4px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
@@ -422,9 +507,10 @@
}
.badge {
- background-color: $gray-dark;
- color: $layout-link-gray;
+ background-color: $gray-darker;
+ color: $gl-text-color-light;
font-weight: normal;
+ margin-left: $btn-xs-side-margin;
}
}
@@ -438,46 +524,49 @@
&::after {
content: '';
position: absolute;
- top: 50%;
- right: -69px;
+ top: 48%;
+ right: -48px;
border-top: 2px solid $border-color;
- width: 69px;
+ width: 48px;
height: 1px;
}
}
// Connect each build (except for first) with curved lines
&:not(:first-child) {
- &::after, &::before {
+ &::after,
+ &::before {
content: '';
- top: -47px;
+ top: -49px;
position: absolute;
border-bottom: 2px solid $border-color;
- width: 20px;
- height: 65px;
+ width: 25px;
+ height: 69px;
}
// Right connecting curves
&::after {
- right: -20px;
+ right: -25px;
border-right: 2px solid $border-color;
- border-radius: 0 0 15px;
+ border-radius: 0 0 20px;
}
// Left connecting curves
&::before {
- left: -20px;
+ left: -25px;
border-left: 2px solid $border-color;
- border-radius: 0 0 0 15px;
+ border-radius: 0 0 0 20px;
}
}
// Connect second build to first build with smaller curved line
&:nth-child(2) {
- &::after, &::before {
+ &::after,
+ &::before {
height: 29px;
- top: -10px;
+ top: -9px;
}
+
.curve {
display: block;
}
@@ -488,7 +577,7 @@
.build {
// Remove right connecting horizontal line from first build in last stage
&:first-child {
- &::after, &::before {
+ &::after {
border: none;
}
}
@@ -534,20 +623,20 @@
width: 21px;
height: 25px;
position: absolute;
- top: -29px;
+ top: -32px;
border-top: 2px solid $border-color;
}
&::after {
- left: -39px;
+ left: -44px;
border-right: 2px solid $border-color;
- border-radius: 0 15px;
+ border-radius: 0 20px;
}
&::before {
- right: -39px;
+ right: -44px;
border-left: 2px solid $border-color;
- border-radius: 15px 0 0;
+ border-radius: 20px 0 0;
}
}
}
@@ -563,25 +652,34 @@
}
}
-.pipelines.tab-pane {
+.tab-pane {
- .content-list.pipelines {
- overflow: auto;
- }
+ &.pipelines {
- .stage {
- max-width: 100px;
- width: 100px;
- }
+ .ci-table {
+ min-width: 900px;
+ }
- .pipeline-actions {
- min-width: initial;
+ .content-list.pipelines {
+ overflow: auto;
+ }
+
+ .stage {
+ max-width: 100px;
+ width: 100px;
+ }
+
+ .pipeline-actions {
+ min-width: initial;
+ }
}
-}
-.ci-status-icon-created {
+ &.builds {
- svg {
- fill: $gray-darkest;
+ .ci-table {
+ tr {
+ height: 71px;
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 6f58203f49c..6fab97a71aa 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -23,6 +23,10 @@
color: $md-link-color;
}
+.private-tokens-reset div.reset-action:not(:first-child) {
+ padding-top: 15px;
+}
+
.oauth-buttons {
.btn-group {
margin-right: 10px;
@@ -77,14 +81,14 @@
// Middle dot divider between each element in a list of items.
.middle-dot-divider {
- &:after {
+ &::after {
content: "\00B7"; // Middle Dot
padding: 0 6px;
font-weight: bold;
}
&:last-child {
- &:after {
+ &::after {
content: "";
padding: 0;
}
@@ -93,8 +97,9 @@
.profile-user-bio {
// Limits the width of the user bio for readability.
- max-width: 750px;
- margin: auto;
+ max-width: 600px;
+ margin: 10px auto;
+ padding: 0 16px;
}
.user-avatar-button {
@@ -212,25 +217,48 @@
}
.user-profile {
+
+ .cover-controls a {
+ margin-left: 5px;
+ }
+
+ .profile-header {
+ margin: 0 auto;
+
+ .avatar-holder {
+ width: 90px;
+ margin: 0 auto 10px;
+ }
+ }
+
@media (max-width: $screen-xs-max) {
+
.cover-block {
padding-top: 20px;
}
.cover-controls {
position: static;
+ padding: 0 16px;
margin-bottom: 20px;
+ display: -webkit-flex;
+ display: flex;
.btn {
- display: inline-block;
- width: 46%;
+ -webkit-flex-grow: 1;
+ flex-grow: 1;
+
+ &:first-child {
+ margin-left: 0;
+ }
}
}
}
}
table.u2f-registrations {
- th:not(:last-child), td:not(:last-child) {
+ th:not(:last-child),
+ td:not(:last-child) {
border-right: solid 1px transparent;
}
} \ No newline at end of file
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
index e5859fe7384..f8da0983b77 100644
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ b/app/assets/stylesheets/pages/profiles/preferences.scss
@@ -4,7 +4,7 @@
text-align: center;
.preview {
- @include border-radius(4px);
+ border-radius: 4px;
height: 80px;
margin-bottom: 10px;
@@ -47,7 +47,7 @@
width: 160px;
img {
- @include border-radius(4px);
+ border-radius: 4px;
max-width: 100%;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index db46d8072ce..19a7a97ea0d 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -6,56 +6,79 @@
}
}
-.no-ssh-key-message, .project-limit-message {
+.no-ssh-key-message,
+.project-limit-message {
background-color: #f28d35;
margin-bottom: 0;
}
.new_project,
.edit-project {
+
fieldset {
- &.features .control-label {
- font-weight: normal;
+
+ &.features {
+
+ .label-light {
+ margin-bottom: 0;
+ }
+
+ .help-block {
+ margin-top: 0;
+ }
}
+
.form-group {
margin-bottom: 5px;
}
+
&> .form-group {
padding-left: 0;
}
}
+
.help-block {
margin-bottom: 10px;
}
+
.project-path {
padding-right: 0;
+
.form-control {
border-radius: $border-radius-base;
}
}
+
.input-group > div {
+
&:last-child {
padding-right: 0;
}
}
+
@media (max-width: $screen-xs-max) {
.input-group > div {
+
margin-bottom: 14px;
+
&:last-child {
margin-bottom: 0;
}
}
+
fieldset > .form-group:first-child {
padding-right: 0;
}
}
.input-group-addon {
+
&.static-namespace {
height: 35px;
border-radius: 3px;
border: 1px solid #e5e5e5;
}
+
&+ .select2 a {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
@@ -63,7 +86,8 @@
}
}
-.project-home-panel {
+.project-home-panel,
+.group-home-panel {
padding-top: 24px;
padding-bottom: 24px;
@@ -71,17 +95,19 @@
border-bottom: 1px solid $border-color;
}
- .project-avatar {
+ .project-avatar,
+ .group-avatar {
float: none;
- margin-left: auto;
- margin-right: auto;
+ margin: 0 auto;
+ border: none;
&.identicon {
border-radius: 50%;
}
}
- .project-title {
+ .project-title,
+ .group-title {
margin-top: 10px;
margin-bottom: 10px;
font-size: 24px;
@@ -95,10 +121,11 @@
}
}
- .project-home-desc {
+ .project-home-desc,
+ .group-home-desc {
margin-left: auto;
margin-right: auto;
- margin-bottom: 15px;
+ margin-bottom: 0;
max-width: 700px;
> p {
@@ -118,13 +145,22 @@
}
}
-.project-repo-buttons {
- font-size: 0;
+.nav > .project-repo-buttons {
+ margin-top: 0;
+}
+
+.project-repo-buttons,
+.group-buttons {
+ margin-top: 15px;
.btn {
@include btn-gray;
padding: 3px 10px;
+ &:last-child {
+ margin-left: 0;
+ }
+
.fa {
color: $layout-link-gray;
}
@@ -145,11 +181,19 @@
}
}
- .project-repo-btn-group,
- .notification-dropdown {
+ .download-button,
+ .dropdown-toggle,
+ .notification-dropdown,
+ .project-dropdown {
margin-left: 10px;
}
+ .download-button {
+ @media (max-width: $screen-lg-min) {
+ margin-left: 0;
+ }
+ }
+
.count-buttons {
display: inline-block;
vertical-align: top;
@@ -169,7 +213,7 @@
margin-left: 4px;
.arrow {
- &:before {
+ &::before {
content: '';
display: inline-block;
position: absolute;
@@ -185,7 +229,7 @@
pointer-events: none;
}
- &:after {
+ &::after {
content: '';
position: absolute;
width: 0;
@@ -200,6 +244,7 @@
pointer-events: none;
}
}
+
.count {
@include btn-gray;
display: inline-block;
@@ -326,7 +371,7 @@ a.deploy-project-label {
line-height: 36px;
margin: 0;
- > li + li:before {
+ > li + li::before {
padding: 0 3px;
color: #999;
}
@@ -353,35 +398,41 @@ a.deploy-project-label {
justify-content: flex-start;
.fork-thumbnail {
- @include border-radius($border-radius-base);
+ border-radius: $border-radius-base;
background-color: $white-light;
border: 1px solid $border-white-light;
height: 202px;
margin: $gl-padding;
text-align: center;
width: 169px;
- &:hover, &.forked {
+
+ &:hover,
+ &.forked {
background-color: $row-hover;
border-color: $row-hover-border;
}
+
.no-avatar {
width: 100px;
height: 100px;
background-color: $gray-light;
border: 1px solid $gray-dark;
margin: 0 auto;
- @include border-radius(50%);
+ border-radius: 50%;
+
i {
font-size: 100px;
color: $gray-dark;
}
}
+
a {
display: block;
width: 100%;
height: 100%;
padding-top: $gl-padding;
color: $gl-gray;
+
.caption {
min-height: 30px;
padding: $gl-padding 0;
@@ -389,7 +440,7 @@ a.deploy-project-label {
}
img {
- @include border-radius(50%);
+ border-radius: 50%;
max-width: 100px;
}
}
@@ -427,6 +478,20 @@ a.deploy-project-label {
}
}
+.page-sidebar-pinned {
+ .project-stats .nav > li.right {
+ @media (min-width: $screen-lg-min) {
+ float: none;
+ }
+ }
+
+ .download-button {
+ @media (min-width: $screen-lg-min) {
+ margin-left: 0;
+ }
+ }
+}
+
.project-stats {
font-size: 0;
border-bottom: 1px solid $border-color;
@@ -443,12 +508,12 @@ a.deploy-project-label {
margin-right: $gl-padding;
}
- &.project-repo-buttons-right {
- margin-top: 10px;
+ &.right {
+ vertical-align: top;
+ margin-top: 0;
- @media (min-width: $screen-md-min) {
+ @media (min-width: $screen-lg-min) {
float: right;
- margin-top: 0;
}
}
}
@@ -495,7 +560,7 @@ pre.light-well {
}
.light-well {
- @include border-radius (2px);
+ border-radius: 2px;
color: #5b6169;
font-size: 13px;
@@ -643,6 +708,7 @@ pre.light-well {
.clone-options {
display: table-cell;
+
a.btn {
width: 100%;
}
@@ -704,7 +770,8 @@ pre.light-well {
.table-bordered {
border-radius: 1px;
- th:not(:last-child), td:not(:last-child) {
+ th:not(:last-child),
+ td:not(:last-child) {
border-right: solid 1px transparent;
}
}
@@ -727,7 +794,8 @@ pre.light-well {
}
}
-.project-refs-form .dropdown-menu, .dropdown-menu-projects {
+.project-refs-form .dropdown-menu,
+.dropdown-menu-projects {
width: 300px;
@media (min-width: $screen-sm-min) {
@@ -756,7 +824,7 @@ pre.light-well {
top: 7px;
color: $location-icon-color;
- &:before {
+ &::before {
font-family: FontAwesome;
font-weight: normal;
font-style: normal;
@@ -770,3 +838,41 @@ pre.light-well {
}
}
}
+
+.project-path {
+ .form-control {
+ min-width: 100px;
+ }
+
+ .select2-choice {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+}
+
+.project-home-empty {
+ border-top: 0;
+
+ .container-fluid {
+ background: none;
+ }
+
+ p {
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 650px;
+ }
+}
+
+.project-feature-nested {
+ @media (min-width: $screen-sm-min) {
+ padding-left: 45px;
+ }
+}
+
+.project-repo-select {
+ &.disabled {
+ opacity: 0.5;
+ pointer-events: none;
+ }
+}
diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss
index eec22c5dc96..7b3878c91df 100644
--- a/app/assets/stylesheets/pages/runners.scss
+++ b/app/assets/stylesheets/pages/runners.scss
@@ -6,6 +6,7 @@
&.runner-state-shared {
background: #32b186;
}
+
&.runner-state-specific {
background: #3498db;
}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 436fb00ba2e..63d0a34e610 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -8,6 +8,10 @@
border-bottom: none;
}
}
+
+ .blob-result {
+ margin: 5px 0;
+ }
}
.search {
@@ -21,6 +25,11 @@
padding: 4px;
width: $search-input-width;
line-height: 24px;
+
+ &:hover {
+ border-color: lighten($dropdown-input-focus-border, 20%);
+ box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
+ }
}
.location-text {
@@ -28,11 +37,9 @@
}
.search-input {
- padding-right: 20px;
border: none;
font-size: 14px;
- outline: none;
- padding: 0;
+ padding: 0 20px 0 0;
margin-left: 5px;
line-height: 25px;
width: 98%;
@@ -65,13 +72,14 @@
.search-input-wrap {
width: 100%;
- .search-icon, .clear-icon {
+ .search-icon,
+ .clear-icon {
position: absolute;
right: 5px;
top: 0;
color: $location-icon-color;
- &:before {
+ &::before {
font-family: FontAwesome;
font-weight: normal;
font-style: normal;
@@ -103,7 +111,7 @@
// Custom dropdown positioning
.dropdown-menu {
- top: 30px;
+ top: 37px;
left: -5px;
padding: 0;
@@ -185,7 +193,8 @@
padding-right: $gl-padding + 15px;
}
- .btn-search, .btn-new {
+ .btn-search,
+ .btn-new {
width: 100%;
margin-top: 5px;
@@ -227,6 +236,5 @@
&:hover,
&:focus {
color: $gl-link-color;
- outline: none;
}
}
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 2e8f356298d..51c926608f9 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -20,3 +20,7 @@
.danger-title {
color: $gl-danger;
}
+
+.service-settings .control-label {
+ padding-top: 0;
+}
diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss
index 5270aea4e79..857eb76131a 100644
--- a/app/assets/stylesheets/pages/snippets.scss
+++ b/app/assets/stylesheets/pages/snippets.scss
@@ -12,11 +12,18 @@
.snippet-file-content {
border-radius: 3px;
+ margin-bottom: $gl-padding;
+
.btn-clipboard {
@extend .btn;
}
}
+.project-snippets .awards {
+ border-bottom: 1px solid $table-border-color;
+ padding-bottom: $gl-padding;
+}
+
.snippet-title {
font-size: 24px;
font-weight: 600;
@@ -29,3 +36,7 @@
float: right;
}
}
+
+.snippet-scope-menu .btn-new {
+ margin-top: 15px;
+}
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 0ee7ceecae5..4c258bae1f4 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -4,84 +4,115 @@
margin-right: 10px;
border: 1px solid #eee;
white-space: nowrap;
- @include border-radius(4px);
+ border-radius: 4px;
- &:hover {
+ &:hover,
+ &:focus {
text-decoration: none;
}
+ svg {
+ height: 13px;
+ width: 13px;
+ position: relative;
+ top: 1px;
+ margin-right: 3px;
+ overflow: visible;
+ }
+
&.ci-failed {
color: $gl-danger;
border-color: $gl-danger;
+
+ &:not(span):hover {
+ background-color: rgba( $gl-danger, .07);
+ }
+
+ svg {
+ fill: $gl-danger;
+ }
}
&.ci-success,
&.ci-success_with_warnings {
color: $gl-success;
border-color: $gl-success;
+
+ &:not(span):hover {
+ background-color: rgba( $gl-success, .07);
+ }
+
+ svg {
+ fill: $gl-success;
+ }
}
&.ci-info {
color: $gl-info;
border-color: $gl-info;
+
+ &:not(span):hover {
+ background-color: rgba( $gl-info, .07);
+ }
+
+ svg {
+ fill: $gl-info;
+ }
}
&.ci-canceled,
- &.ci-skipped,
&.ci-disabled {
color: $gl-gray;
border-color: $gl-gray;
+
+ &:not(span):hover {
+ background-color: rgba( $gl-gray, .07);
+ }
+
+ svg {
+ fill: $gl-gray;
+ }
}
&.ci-pending {
color: $gl-warning;
border-color: $gl-warning;
+
+ &:not(span):hover {
+ background-color: rgba( $gl-warning, .07);
+ }
+
+ svg {
+ fill: $gl-warning;
+ }
}
&.ci-running {
color: $blue-normal;
border-color: $blue-normal;
+
+ &:not(span):hover {
+ background-color: rgba( $blue-normal, .07);
+ }
+
+ svg {
+ fill: $blue-normal;
+ }
}
- &.ci-created {
+ &.ci-created,
+ &.ci-skipped {
color: $table-text-gray;
border-color: $table-text-gray;
+ &:not(span):hover {
+ background-color: rgba( $table-text-gray, .07);
+ }
+
svg {
fill: $table-text-gray;
}
}
-
- svg {
- height: 13px;
- width: 13px;
- position: relative;
- top: 1px;
- margin: 0 3px;
- overflow: visible;
- }
- }
-
- .ci-status-icon-success {
- color: $gl-success;
- }
- .ci-status-icon-failed {
- color: $gl-danger;
- }
-
- .ci-status-icon-pending,
- .ci-status-icon-success_with_warning {
- color: $gl-warning;
- }
-
- .ci-status-icon-running {
- color: $blue-normal;
- }
- .ci-status-icon-canceled,
- .ci-status-icon-disabled,
- .ci-status-icon-not-found,
- .ci-status-icon-skipped {
- color: $gl-gray;
}
}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 68a5d1ae06c..b3aef2fdd32 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -51,6 +51,7 @@
-webkit-flex-direction: column;
flex-direction: column;
margin-left: 10px;
+ min-width: 55px;
}
.todo-item {
@@ -120,6 +121,14 @@
}
}
+@media (max-width: $screen-sm-max) {
+ .todos-filters {
+ .dropdown-menu-toggle {
+ width: 135px;
+ }
+ }
+}
+
@media (max-width: $screen-xs-max) {
.todo {
.avatar {
@@ -141,4 +150,74 @@
padding-left: 10px;
}
}
+
+ .todos-filters {
+ .row-content-block {
+ padding-bottom: 50px;
+ }
+
+ .dropdown-menu-toggle {
+ width: 100%;
+ }
+ }
+}
+
+.todos-empty {
+ display: -webkit-flex;
+ display: flex;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ max-width: 900px;
+ margin-left: auto;
+ margin-right: auto;
+
+ @media (min-width: $screen-sm-min) {
+ -webkit-flex-direction: row;
+ flex-direction: row;
+ padding-top: 80px;
+ }
+}
+
+.todos-empty-content {
+ -webkit-align-self: center;
+ align-self: center;
+ max-width: 480px;
+ margin-right: 20px;
+}
+
+.todos-empty-hero {
+ width: 200px;
+ margin-left: auto;
+ margin-right: auto;
+
+ @media (min-width: $screen-sm-min) {
+ width: 300px;
+ margin-right: 0;
+ -webkit-order: 2;
+ order: 2;
+ }
+}
+
+.todos-all-done {
+ padding-top: 20px;
+
+ @media (min-width: $screen-sm-min) {
+ padding-top: 50px;
+ }
+
+ > svg {
+ display: block;
+ max-width: 300px;
+ margin: 0 auto 20px;
+ }
+
+ p {
+ max-width: 470px;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ a {
+ font-weight: 600;
+ }
}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 1778c069706..2b836fa1f4a 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -5,6 +5,7 @@
.file-finder {
width: 50%;
+
.file-finder-input {
width: 95%;
display: inline-block;
@@ -22,12 +23,18 @@
border-bottom: 1px solid $table-border-gray;
border-top: 1px solid $table-border-gray;
- td, th {
+ td,
+ th {
line-height: 21px;
}
.last-commit {
- @include str-truncated(60%);
+ @include str-truncated(506px);
+
+ @media (min-width: $screen-sm-max) and (max-width: $screen-md-max) {
+ @include str-truncated(450px);
+ }
+
}
.commit-history-link-spacer {
@@ -55,11 +62,21 @@
}
.tree-item {
+ .link-container {
+ padding: 0;
+
+ a {
+ padding: 10px $gl-padding;
+ display: block;
+ }
+ }
+
.tree-item-file-name {
max-width: 320px;
vertical-align: middle;
- i, a {
+ i,
+ a {
color: $gl-dark-link-color;
}
@@ -154,4 +171,8 @@
margin-top: 11px;
position: relative;
z-index: 2;
+
+ .download-button {
+ margin-left: $btn-side-margin;
+ }
}
diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss
index 587bd6a1e8a..e73cecc92be 100644
--- a/app/assets/stylesheets/pages/ui_dev_kit.scss
+++ b/app/assets/stylesheets/pages/ui_dev_kit.scss
@@ -5,7 +5,7 @@
}
.example {
- &:before {
+ &::before {
content: "Example";
color: #bbb;
}
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
index c9846103762..3fa7fa3d7e3 100644
--- a/app/assets/stylesheets/pages/xterm.scss
+++ b/app/assets/stylesheets/pages/xterm.scss
@@ -23,15 +23,19 @@
.term-bold {
font-weight: bold;
}
+
.term-italic {
font-style: italic;
}
+
.term-conceal {
visibility: hidden;
}
+
.term-underline {
text-decoration: underline;
}
+
.term-cross {
text-decoration: line-through;
}
@@ -39,48 +43,63 @@
.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;
}
@@ -88,818 +107,1087 @@
.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: #000;
}
+
.xterm-fg-1 {
color: #800000;
}
+
.xterm-fg-2 {
color: #008000;
}
+
.xterm-fg-3 {
color: #808000;
}
+
.xterm-fg-4 {
color: #000080;
}
+
.xterm-fg-5 {
color: #800080;
}
+
.xterm-fg-6 {
color: #008080;
}
+
.xterm-fg-7 {
color: #c0c0c0;
}
+
.xterm-fg-8 {
color: #808080;
}
+
.xterm-fg-9 {
color: #f00;
}
+
.xterm-fg-10 {
color: #0f0;
}
+
.xterm-fg-11 {
color: #ff0;
}
+
.xterm-fg-12 {
color: #00f;
}
+
.xterm-fg-13 {
color: #f0f;
}
+
.xterm-fg-14 {
color: #0ff;
}
+
.xterm-fg-15 {
color: #fff;
}
+
.xterm-fg-16 {
color: #000;
}
+
.xterm-fg-17 {
color: #00005f;
}
+
.xterm-fg-18 {
color: #000087;
}
+
.xterm-fg-19 {
color: #0000af;
}
+
.xterm-fg-20 {
color: #0000d7;
}
+
.xterm-fg-21 {
color: #00f;
}
+
.xterm-fg-22 {
color: #005f00;
}
+
.xterm-fg-23 {
color: #005f5f;
}
+
.xterm-fg-24 {
color: #005f87;
}
+
.xterm-fg-25 {
color: #005faf;
}
+
.xterm-fg-26 {
color: #005fd7;
}
+
.xterm-fg-27 {
color: #005fff;
}
+
.xterm-fg-28 {
color: #008700;
}
+
.xterm-fg-29 {
color: #00875f;
}
+
.xterm-fg-30 {
color: #008787;
}
+
.xterm-fg-31 {
color: #0087af;
}
+
.xterm-fg-32 {
color: #0087d7;
}
+
.xterm-fg-33 {
color: #0087ff;
}
+
.xterm-fg-34 {
color: #00af00;
}
+
.xterm-fg-35 {
color: #00af5f;
}
+
.xterm-fg-36 {
color: #00af87;
}
+
.xterm-fg-37 {
color: #00afaf;
}
+
.xterm-fg-38 {
color: #00afd7;
}
+
.xterm-fg-39 {
color: #00afff;
}
+
.xterm-fg-40 {
color: #00d700;
}
+
.xterm-fg-41 {
color: #00d75f;
}
+
.xterm-fg-42 {
color: #00d787;
}
+
.xterm-fg-43 {
color: #00d7af;
}
+
.xterm-fg-44 {
color: #00d7d7;
}
+
.xterm-fg-45 {
color: #00d7ff;
}
+
.xterm-fg-46 {
color: #0f0;
}
+
.xterm-fg-47 {
color: #00ff5f;
}
+
.xterm-fg-48 {
color: #00ff87;
}
+
.xterm-fg-49 {
color: #00ffaf;
}
+
.xterm-fg-50 {
color: #00ffd7;
}
+
.xterm-fg-51 {
color: #0ff;
}
+
.xterm-fg-52 {
color: #5f0000;
}
+
.xterm-fg-53 {
color: #5f005f;
}
+
.xterm-fg-54 {
color: #5f0087;
}
+
.xterm-fg-55 {
color: #5f00af;
}
+
.xterm-fg-56 {
color: #5f00d7;
}
+
.xterm-fg-57 {
color: #5f00ff;
}
+
.xterm-fg-58 {
color: #5f5f00;
}
+
.xterm-fg-59 {
color: #5f5f5f;
}
+
.xterm-fg-60 {
color: #5f5f87;
}
+
.xterm-fg-61 {
color: #5f5faf;
}
+
.xterm-fg-62 {
color: #5f5fd7;
}
+
.xterm-fg-63 {
color: #5f5fff;
}
+
.xterm-fg-64 {
color: #5f8700;
}
+
.xterm-fg-65 {
color: #5f875f;
}
+
.xterm-fg-66 {
color: #5f8787;
}
+
.xterm-fg-67 {
color: #5f87af;
}
+
.xterm-fg-68 {
color: #5f87d7;
}
+
.xterm-fg-69 {
color: #5f87ff;
}
+
.xterm-fg-70 {
color: #5faf00;
}
+
.xterm-fg-71 {
color: #5faf5f;
}
+
.xterm-fg-72 {
color: #5faf87;
}
+
.xterm-fg-73 {
color: #5fafaf;
}
+
.xterm-fg-74 {
color: #5fafd7;
}
+
.xterm-fg-75 {
color: #5fafff;
}
+
.xterm-fg-76 {
color: #5fd700;
}
+
.xterm-fg-77 {
color: #5fd75f;
}
+
.xterm-fg-78 {
color: #5fd787;
}
+
.xterm-fg-79 {
color: #5fd7af;
}
+
.xterm-fg-80 {
color: #5fd7d7;
}
+
.xterm-fg-81 {
color: #5fd7ff;
}
+
.xterm-fg-82 {
color: #5fff00;
}
+
.xterm-fg-83 {
color: #5fff5f;
}
+
.xterm-fg-84 {
color: #5fff87;
}
+
.xterm-fg-85 {
color: #5fffaf;
}
+
.xterm-fg-86 {
color: #5fffd7;
}
+
.xterm-fg-87 {
color: #5fffff;
}
+
.xterm-fg-88 {
color: #870000;
}
+
.xterm-fg-89 {
color: #87005f;
}
+
.xterm-fg-90 {
color: #870087;
}
+
.xterm-fg-91 {
color: #8700af;
}
+
.xterm-fg-92 {
color: #8700d7;
}
+
.xterm-fg-93 {
color: #8700ff;
}
+
.xterm-fg-94 {
color: #875f00;
}
+
.xterm-fg-95 {
color: #875f5f;
}
+
.xterm-fg-96 {
color: #875f87;
}
+
.xterm-fg-97 {
color: #875faf;
}
+
.xterm-fg-98 {
color: #875fd7;
}
+
.xterm-fg-99 {
color: #875fff;
}
+
.xterm-fg-100 {
color: #878700;
}
+
.xterm-fg-101 {
color: #87875f;
}
+
.xterm-fg-102 {
color: #878787;
}
+
.xterm-fg-103 {
color: #8787af;
}
+
.xterm-fg-104 {
color: #8787d7;
}
+
.xterm-fg-105 {
color: #8787ff;
}
+
.xterm-fg-106 {
color: #87af00;
}
+
.xterm-fg-107 {
color: #87af5f;
}
+
.xterm-fg-108 {
color: #87af87;
}
+
.xterm-fg-109 {
color: #87afaf;
}
+
.xterm-fg-110 {
color: #87afd7;
}
+
.xterm-fg-111 {
color: #87afff;
}
+
.xterm-fg-112 {
color: #87d700;
}
+
.xterm-fg-113 {
color: #87d75f;
}
+
.xterm-fg-114 {
color: #87d787;
}
+
.xterm-fg-115 {
color: #87d7af;
}
+
.xterm-fg-116 {
color: #87d7d7;
}
+
.xterm-fg-117 {
color: #87d7ff;
}
+
.xterm-fg-118 {
color: #87ff00;
}
+
.xterm-fg-119 {
color: #87ff5f;
}
+
.xterm-fg-120 {
color: #87ff87;
}
+
.xterm-fg-121 {
color: #87ffaf;
}
+
.xterm-fg-122 {
color: #87ffd7;
}
+
.xterm-fg-123 {
color: #87ffff;
}
+
.xterm-fg-124 {
color: #af0000;
}
+
.xterm-fg-125 {
color: #af005f;
}
+
.xterm-fg-126 {
color: #af0087;
}
+
.xterm-fg-127 {
color: #af00af;
}
+
.xterm-fg-128 {
color: #af00d7;
}
+
.xterm-fg-129 {
color: #af00ff;
}
+
.xterm-fg-130 {
color: #af5f00;
}
+
.xterm-fg-131 {
color: #af5f5f;
}
+
.xterm-fg-132 {
color: #af5f87;
}
+
.xterm-fg-133 {
color: #af5faf;
}
+
.xterm-fg-134 {
color: #af5fd7;
}
+
.xterm-fg-135 {
color: #af5fff;
}
+
.xterm-fg-136 {
color: #af8700;
}
+
.xterm-fg-137 {
color: #af875f;
}
+
.xterm-fg-138 {
color: #af8787;
}
+
.xterm-fg-139 {
color: #af87af;
}
+
.xterm-fg-140 {
color: #af87d7;
}
+
.xterm-fg-141 {
color: #af87ff;
}
+
.xterm-fg-142 {
color: #afaf00;
}
+
.xterm-fg-143 {
color: #afaf5f;
}
+
.xterm-fg-144 {
color: #afaf87;
}
+
.xterm-fg-145 {
color: #afafaf;
}
+
.xterm-fg-146 {
color: #afafd7;
}
+
.xterm-fg-147 {
color: #afafff;
}
+
.xterm-fg-148 {
color: #afd700;
}
+
.xterm-fg-149 {
color: #afd75f;
}
+
.xterm-fg-150 {
color: #afd787;
}
+
.xterm-fg-151 {
color: #afd7af;
}
+
.xterm-fg-152 {
color: #afd7d7;
}
+
.xterm-fg-153 {
color: #afd7ff;
}
+
.xterm-fg-154 {
color: #afff00;
}
+
.xterm-fg-155 {
color: #afff5f;
}
+
.xterm-fg-156 {
color: #afff87;
}
+
.xterm-fg-157 {
color: #afffaf;
}
+
.xterm-fg-158 {
color: #afffd7;
}
+
.xterm-fg-159 {
color: #afffff;
}
+
.xterm-fg-160 {
color: #d70000;
}
+
.xterm-fg-161 {
color: #d7005f;
}
+
.xterm-fg-162 {
color: #d70087;
}
+
.xterm-fg-163 {
color: #d700af;
}
+
.xterm-fg-164 {
color: #d700d7;
}
+
.xterm-fg-165 {
color: #d700ff;
}
+
.xterm-fg-166 {
color: #d75f00;
}
+
.xterm-fg-167 {
color: #d75f5f;
}
+
.xterm-fg-168 {
color: #d75f87;
}
+
.xterm-fg-169 {
color: #d75faf;
}
+
.xterm-fg-170 {
color: #d75fd7;
}
+
.xterm-fg-171 {
color: #d75fff;
}
+
.xterm-fg-172 {
color: #d78700;
}
+
.xterm-fg-173 {
color: #d7875f;
}
+
.xterm-fg-174 {
color: #d78787;
}
+
.xterm-fg-175 {
color: #d787af;
}
+
.xterm-fg-176 {
color: #d787d7;
}
+
.xterm-fg-177 {
color: #d787ff;
}
+
.xterm-fg-178 {
color: #d7af00;
}
+
.xterm-fg-179 {
color: #d7af5f;
}
+
.xterm-fg-180 {
color: #d7af87;
}
+
.xterm-fg-181 {
color: #d7afaf;
}
+
.xterm-fg-182 {
color: #d7afd7;
}
+
.xterm-fg-183 {
color: #d7afff;
}
+
.xterm-fg-184 {
color: #d7d700;
}
+
.xterm-fg-185 {
color: #d7d75f;
}
+
.xterm-fg-186 {
color: #d7d787;
}
+
.xterm-fg-187 {
color: #d7d7af;
}
+
.xterm-fg-188 {
color: #d7d7d7;
}
+
.xterm-fg-189 {
color: #d7d7ff;
}
+
.xterm-fg-190 {
color: #d7ff00;
}
+
.xterm-fg-191 {
color: #d7ff5f;
}
+
.xterm-fg-192 {
color: #d7ff87;
}
+
.xterm-fg-193 {
color: #d7ffaf;
}
+
.xterm-fg-194 {
color: #d7ffd7;
}
+
.xterm-fg-195 {
color: #d7ffff;
}
+
.xterm-fg-196 {
color: #f00;
}
+
.xterm-fg-197 {
color: #ff005f;
}
+
.xterm-fg-198 {
color: #ff0087;
}
+
.xterm-fg-199 {
color: #ff00af;
}
+
.xterm-fg-200 {
color: #ff00d7;
}
+
.xterm-fg-201 {
color: #f0f;
}
+
.xterm-fg-202 {
color: #ff5f00;
}
+
.xterm-fg-203 {
color: #ff5f5f;
}
+
.xterm-fg-204 {
color: #ff5f87;
}
+
.xterm-fg-205 {
color: #ff5faf;
}
+
.xterm-fg-206 {
color: #ff5fd7;
}
+
.xterm-fg-207 {
color: #ff5fff;
}
+
.xterm-fg-208 {
color: #ff8700;
}
+
.xterm-fg-209 {
color: #ff875f;
}
+
.xterm-fg-210 {
color: #ff8787;
}
+
.xterm-fg-211 {
color: #ff87af;
}
+
.xterm-fg-212 {
color: #ff87d7;
}
+
.xterm-fg-213 {
color: #ff87ff;
}
+
.xterm-fg-214 {
color: #ffaf00;
}
+
.xterm-fg-215 {
color: #ffaf5f;
}
+
.xterm-fg-216 {
color: #ffaf87;
}
+
.xterm-fg-217 {
color: #ffafaf;
}
+
.xterm-fg-218 {
color: #ffafd7;
}
+
.xterm-fg-219 {
color: #ffafff;
}
+
.xterm-fg-220 {
color: #ffd700;
}
+
.xterm-fg-221 {
color: #ffd75f;
}
+
.xterm-fg-222 {
color: #ffd787;
}
+
.xterm-fg-223 {
color: #ffd7af;
}
+
.xterm-fg-224 {
color: #ffd7d7;
}
+
.xterm-fg-225 {
color: #ffd7ff;
}
+
.xterm-fg-226 {
color: #ff0;
}
+
.xterm-fg-227 {
color: #ffff5f;
}
+
.xterm-fg-228 {
color: #ffff87;
}
+
.xterm-fg-229 {
color: #ffffaf;
}
+
.xterm-fg-230 {
color: #ffffd7;
}
+
.xterm-fg-231 {
color: #fff;
}
+
.xterm-fg-232 {
color: #080808;
}
+
.xterm-fg-233 {
color: #121212;
}
+
.xterm-fg-234 {
color: #1c1c1c;
}
+
.xterm-fg-235 {
color: #262626;
}
+
.xterm-fg-236 {
color: #303030;
}
+
.xterm-fg-237 {
color: #3a3a3a;
}
+
.xterm-fg-238 {
color: #444;
}
+
.xterm-fg-239 {
color: #4e4e4e;
}
+
.xterm-fg-240 {
color: #585858;
}
+
.xterm-fg-241 {
color: #626262;
}
+
.xterm-fg-242 {
color: #6c6c6c;
}
+
.xterm-fg-243 {
color: #767676;
}
+
.xterm-fg-244 {
color: #808080;
}
+
.xterm-fg-245 {
color: #8a8a8a;
}
+
.xterm-fg-246 {
color: #949494;
}
+
.xterm-fg-247 {
color: #9e9e9e;
}
+
.xterm-fg-248 {
color: #a8a8a8;
}
+
.xterm-fg-249 {
color: #b2b2b2;
}
+
.xterm-fg-250 {
color: #bcbcbc;
}
+
.xterm-fg-251 {
color: #c6c6c6;
}
+
.xterm-fg-252 {
color: #d0d0d0;
}
+
.xterm-fg-253 {
color: #dadada;
}
+
.xterm-fg-254 {
color: #e4e4e4;
}
+
.xterm-fg-255 {
color: #eee;
}
diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss
index a30b6492572..0ff3c3f5472 100644
--- a/app/assets/stylesheets/print.scss
+++ b/app/assets/stylesheets/print.scss
@@ -1,7 +1,24 @@
-.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; }
-.wiki h1 {font-size: 30px;}
-.wiki h2 {font-size: 22px;}
-.wiki h3 {font-size: 18px; font-weight: bold; }
+.wiki h1,
+.wiki h2,
+.wiki h3,
+.wiki h4,
+.wiki h5,
+.wiki h6 {
+ margin-top: 17px;
+}
+
+.wiki h1 {
+ font-size: 30px;
+}
+
+.wiki h2 {
+ font-size: 22px;
+}
+
+.wiki h3 {
+ font-size: 18px;
+ font-weight: bold;
+}
header,
nav,
@@ -18,7 +35,7 @@ nav.navbar-collapse.collapse,
.nav,
.btn,
ul.notes-form,
-.merge-request-ci-status .ci-status-link:after,
+.merge-request-ci-status .ci-status-link::after,
.issuable-gutter-toggle,
.gutter-toggle,
.issuable-details .content-block-small,