summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintignore1
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock22
-rw-r--r--app/assets/javascripts/abuse_reports.js.es63
-rw-r--r--app/assets/javascripts/admin.js4
-rw-r--r--app/assets/javascripts/api.js6
-rw-r--r--app/assets/javascripts/application.js9
-rw-r--r--app/assets/javascripts/awards_handler.js4
-rw-r--r--app/assets/javascripts/behaviors/autosize.js3
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js3
-rw-r--r--app/assets/javascripts/blob/blob_ci_yaml.js.es64
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js4
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selector.js3
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selectors.js4
-rw-r--r--app/assets/javascripts/blob/blob_license_selector.js3
-rw-r--r--app/assets/javascripts/blob/blob_license_selectors.js.es64
-rw-r--r--app/assets/javascripts/blob/template_selector.js.es63
-rw-r--r--app/assets/javascripts/blob_edit/blob_edit_bundle.js5
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js7
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js.es65
-rw-r--r--app/assets/javascripts/boards/components/board.js.es65
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.js.es65
-rw-r--r--app/assets/javascripts/boards/components/board_card.js.es64
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js.es64
-rw-r--r--app/assets/javascripts/boards/components/board_list.js.es65
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.js.es65
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js.es68
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js.es611
-rw-r--r--app/assets/javascripts/boards/filters/due_date_filters.js.es63
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js.es64
-rw-r--r--app/assets/javascripts/boards/models/issue.js.es67
-rw-r--r--app/assets/javascripts/boards/models/label.js.es63
-rw-r--r--app/assets/javascripts/boards/models/list.js.es65
-rw-r--r--app/assets/javascripts/boards/models/milestone.js.es65
-rw-r--r--app/assets/javascripts/boards/models/user.js.es65
-rw-r--r--app/assets/javascripts/boards/services/board_service.js.es64
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js.es65
-rw-r--r--app/assets/javascripts/boards/vue_resource_interceptor.js.es64
-rw-r--r--app/assets/javascripts/breakpoints.js6
-rw-r--r--app/assets/javascripts/build.js6
-rw-r--r--app/assets/javascripts/build_variables.js.es63
-rw-r--r--app/assets/javascripts/commit.js4
-rw-r--r--app/assets/javascripts/commit/file.js4
-rw-r--r--app/assets/javascripts/commits.js8
-rw-r--r--app/assets/javascripts/compare_autocomplete.js.es63
-rw-r--r--app/assets/javascripts/copy_to_clipboard.js3
-rw-r--r--app/assets/javascripts/create_label.js.es64
-rw-r--r--app/assets/javascripts/diff_notes/components/comment_resolve_btn.js.es65
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js.es68
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_btn.js.es67
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_count.js.es66
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js.es66
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js.es65
-rw-r--r--app/assets/javascripts/diff_notes/mixins/discussion.js.es63
-rw-r--r--app/assets/javascripts/diff_notes/models/discussion.js.es67
-rw-r--r--app/assets/javascripts/diff_notes/models/note.js.es65
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js.es66
-rw-r--r--app/assets/javascripts/diff_notes/stores/comments.js.es65
-rw-r--r--app/assets/javascripts/dispatcher.js.es640
-rw-r--r--app/assets/javascripts/dropzone_input.js3
-rw-r--r--app/assets/javascripts/due_date_select.js.es63
-rw-r--r--app/assets/javascripts/extensions/object.js.es626
-rw-r--r--app/assets/javascripts/files_comment_button.js4
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.es612
-rw-r--r--app/assets/javascripts/gl_dropdown.js7
-rw-r--r--app/assets/javascripts/gl_field_errors.js.es62
-rw-r--r--app/assets/javascripts/gl_form.js6
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors.js7
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_graph.js4
-rw-r--r--app/assets/javascripts/group_label_subscription.js.es63
-rw-r--r--app/assets/javascripts/groups_select.js6
-rw-r--r--app/assets/javascripts/importer_status.js7
-rw-r--r--app/assets/javascripts/issuable.js.es65
-rw-r--r--app/assets/javascripts/issuable_context.js4
-rw-r--r--app/assets/javascripts/issuable_form.js7
-rw-r--r--app/assets/javascripts/issue.js3
-rw-r--r--app/assets/javascripts/issues_bulk_assignment.js.es65
-rw-r--r--app/assets/javascripts/label_manager.js.es65
-rw-r--r--app/assets/javascripts/labels_select.js5
-rw-r--r--app/assets/javascripts/lib/ace.js1
-rw-r--r--app/assets/javascripts/lib/utils/notify.js3
-rw-r--r--app/assets/javascripts/line_highlighter.js3
-rw-r--r--app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es66
-rw-r--r--app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es64
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es66
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es63
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es65
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es65
-rw-r--r--app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es63
-rw-r--r--app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es63
-rw-r--r--app/assets/javascripts/merge_request.js3
-rw-r--r--app/assets/javascripts/merge_request_widget.js.es623
-rw-r--r--app/assets/javascripts/merged_buttons.js3
-rw-r--r--app/assets/javascripts/milestone.js4
-rw-r--r--app/assets/javascripts/milestone_select.js6
-rw-r--r--app/assets/javascripts/namespace_select.js10
-rw-r--r--app/assets/javascripts/network/branch_graph.js4
-rw-r--r--app/assets/javascripts/network/network.js4
-rw-r--r--app/assets/javascripts/network/network_bundle.js5
-rw-r--r--app/assets/javascripts/notes.js14
-rw-r--r--app/assets/javascripts/notifications_dropdown.js4
-rw-r--r--app/assets/javascripts/pipelines.js.es63
-rw-r--r--app/assets/javascripts/preview_markdown.js9
-rw-r--r--app/assets/javascripts/profile/gl_crop.js.es63
-rw-r--r--app/assets/javascripts/profile/profile.js.es64
-rw-r--r--app/assets/javascripts/project.js6
-rw-r--r--app/assets/javascripts/project_find_file.js4
-rw-r--r--app/assets/javascripts/project_import.js4
-rw-r--r--app/assets/javascripts/project_label_subscription.js.es63
-rw-r--r--app/assets/javascripts/project_select.js4
-rw-r--r--app/assets/javascripts/projects_list.js7
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es63
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_create.js.es64
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es63
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit.js.es66
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es63
-rw-r--r--app/assets/javascripts/protected_branches/protected_branches_bundle.js1
-rw-r--r--app/assets/javascripts/render_gfm.js16
-rw-r--r--app/assets/javascripts/render_math.js55
-rw-r--r--app/assets/javascripts/right_sidebar.js4
-rw-r--r--app/assets/javascripts/search.js4
-rw-r--r--app/assets/javascripts/search_autocomplete.js.es63
-rw-r--r--app/assets/javascripts/shortcuts.js6
-rw-r--r--app/assets/javascripts/shortcuts_blob.js4
-rw-r--r--app/assets/javascripts/shortcuts_dashboard_navigation.js4
-rw-r--r--app/assets/javascripts/shortcuts_find_file.js4
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js6
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js4
-rw-r--r--app/assets/javascripts/shortcuts_network.js4
-rw-r--r--app/assets/javascripts/sidebar.js.es64
-rw-r--r--app/assets/javascripts/single_file_diff.js7
-rw-r--r--app/assets/javascripts/snippet/snippet_bundle.js4
-rw-r--r--app/assets/javascripts/snippets_list.js.es63
-rw-r--r--app/assets/javascripts/star.js4
-rw-r--r--app/assets/javascripts/syntax_highlight.js9
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js.es64
-rw-r--r--app/assets/javascripts/templates/issuable_template_selectors.js.es63
-rw-r--r--app/assets/javascripts/todos.js.es67
-rw-r--r--app/assets/javascripts/u2f/authenticate.js6
-rw-r--r--app/assets/javascripts/u2f/error.js4
-rw-r--r--app/assets/javascripts/u2f/register.js6
-rw-r--r--app/assets/javascripts/user.js.es64
-rw-r--r--app/assets/javascripts/user_tabs.js.es63
-rw-r--r--app/assets/javascripts/username_validator.js.es65
-rw-r--r--app/assets/javascripts/users/calendar.js5
-rw-r--r--app/assets/javascripts/users_select.js6
-rw-r--r--app/assets/javascripts/zen_mode.js5
-rw-r--r--app/assets/stylesheets/framework.scss3
-rw-r--r--app/assets/stylesheets/framework/awards.scss6
-rw-r--r--app/assets/stylesheets/framework/blocks.scss4
-rw-r--r--app/assets/stylesheets/framework/buttons.scss22
-rw-r--r--app/assets/stylesheets/framework/callout.scss2
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss29
-rw-r--r--app/assets/stylesheets/framework/emojis.scss (renamed from app/assets/stylesheets/pages/emojis.scss)2
-rw-r--r--app/assets/stylesheets/framework/files.scss2
-rw-r--r--app/assets/stylesheets/framework/forms.scss4
-rw-r--r--app/assets/stylesheets/framework/header.scss18
-rw-r--r--app/assets/stylesheets/framework/icons.scss (renamed from app/assets/stylesheets/pages/icons.scss)0
-rw-r--r--app/assets/stylesheets/framework/images.scss2
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss2
-rw-r--r--app/assets/stylesheets/framework/lists.scss12
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss4
-rw-r--r--app/assets/stylesheets/framework/nav.scss22
-rw-r--r--app/assets/stylesheets/framework/page-header.scss2
-rw-r--r--app/assets/stylesheets/framework/selects.scss4
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss2
-rw-r--r--app/assets/stylesheets/framework/snippets.scss (renamed from app/assets/stylesheets/pages/snippets.scss)10
-rw-r--r--app/assets/stylesheets/framework/tables.scss4
-rw-r--r--app/assets/stylesheets/framework/timeline.scss2
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap.scss4
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap_variables.scss10
-rw-r--r--app/assets/stylesheets/framework/variables.scss64
-rw-r--r--app/assets/stylesheets/framework/wells.scss4
-rw-r--r--app/assets/stylesheets/framework/zen.scss4
-rw-r--r--app/assets/stylesheets/highlight/white.scss6
-rw-r--r--app/assets/stylesheets/mailers/highlighted_diff_email.scss6
-rw-r--r--app/assets/stylesheets/pages/boards.scss4
-rw-r--r--app/assets/stylesheets/pages/builds.scss8
-rw-r--r--app/assets/stylesheets/pages/commits.scss8
-rw-r--r--app/assets/stylesheets/pages/diff.scss8
-rw-r--r--app/assets/stylesheets/pages/editor.scss53
-rw-r--r--app/assets/stylesheets/pages/environments.scss8
-rw-r--r--app/assets/stylesheets/pages/events.scss2
-rw-r--r--app/assets/stylesheets/pages/explore.scss8
-rw-r--r--app/assets/stylesheets/pages/help.scss2
-rw-r--r--app/assets/stylesheets/pages/issuable.scss12
-rw-r--r--app/assets/stylesheets/pages/issues.scss8
-rw-r--r--app/assets/stylesheets/pages/merge_conflicts.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss6
-rw-r--r--app/assets/stylesheets/pages/milestone.scss2
-rw-r--r--app/assets/stylesheets/pages/note_form.scss4
-rw-r--r--app/assets/stylesheets/pages/notes.scss20
-rw-r--r--app/assets/stylesheets/pages/notifications.scss14
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss16
-rw-r--r--app/assets/stylesheets/pages/profile.scss10
-rw-r--r--app/assets/stylesheets/pages/projects.scss10
-rw-r--r--app/assets/stylesheets/pages/search.scss6
-rw-r--r--app/assets/stylesheets/pages/settings.scss2
-rw-r--r--app/assets/stylesheets/pages/status.scss8
-rw-r--r--app/assets/stylesheets/pages/tree.scss10
-rw-r--r--app/assets/stylesheets/pages/xterm.scss2
-rw-r--r--app/controllers/concerns/service_params.rb2
-rw-r--r--app/controllers/projects/notes_controller.rb2
-rw-r--r--app/finders/issues_finder.rb18
-rw-r--r--app/finders/notes_finder.rb113
-rw-r--r--app/models/ci/build.rb32
-rw-r--r--app/models/ci/pipeline.rb18
-rw-r--r--app/models/commit.rb12
-rw-r--r--app/models/concerns/milestoneish.rb2
-rw-r--r--app/models/concerns/token_authenticatable.rb4
-rw-r--r--app/models/environment.rb57
-rw-r--r--app/models/issue.rb55
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/models/note.rb17
-rw-r--r--app/models/project.rb9
-rw-r--r--app/models/project_services/deployment_service.rb11
-rw-r--r--app/models/project_services/kubernetes_service.rb118
-rw-r--r--app/models/project_services/slack_service/pipeline_message.rb2
-rw-r--r--app/models/service.rb1
-rw-r--r--app/services/ci/create_pipeline_builds_service.rb15
-rw-r--r--app/services/ci/image_for_build_service.rb8
-rw-r--r--app/uploaders/artifact_uploader.rb10
-rw-r--r--app/uploaders/attachment_uploader.rb2
-rw-r--r--app/uploaders/avatar_uploader.rb2
-rw-r--r--app/uploaders/file_uploader.rb2
-rw-r--r--app/uploaders/gitlab_uploader.rb11
-rw-r--r--app/uploaders/lfs_object_uploader.rb10
-rw-r--r--app/views/explore/_head.html.haml4
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/merge_requests/_show.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_commits.html.haml4
-rw-r--r--app/views/projects/snippets/show.html.haml5
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/views/snippets/show.html.haml4
-rw-r--r--changelogs/unreleased/22849-ci-build-ref-slug.yml4
-rw-r--r--changelogs/unreleased/22864-add-environment-slug.yml4
-rw-r--r--changelogs/unreleased/22864-kubernetes-service.yml4
-rw-r--r--changelogs/unreleased/24824-dropdown-items-focus.yml4
-rw-r--r--changelogs/unreleased/25534-adding-a-way-to-go-back-on-error-pages.yml4
-rw-r--r--changelogs/unreleased/8003-katex-math.yml4
-rw-r--r--changelogs/unreleased/file-template-dropwdown-proper-position.yml4
-rw-r--r--changelogs/unreleased/fix-slack-pipeline-message-by-api.yml4
-rw-r--r--changelogs/unreleased/gitlab-workhorse-multipart.yml4
-rw-r--r--changelogs/unreleased/jej-24637-move-issue-visible_to_user-to-finder.yml4
-rw-r--r--changelogs/unreleased/jej-note-search-uses-finder.yml4
-rw-r--r--changelogs/unreleased/move-admin-active-tab-spinach-tests-to-rspec.yml4
-rw-r--r--changelogs/unreleased/remove-unnecessary-message-mr-commits-tab.yml4
-rw-r--r--changelogs/unreleased/seed-runner-token.yml5
-rw-r--r--changelogs/unreleased/show-commit-status-from-latest-pipeline.yml4
-rw-r--r--changelogs/unreleased/username-exists-root.yml4
-rw-r--r--config/application.rb6
-rw-r--r--config/initializers/math_lexer.rb2
-rw-r--r--config/initializers/workhorse_multipart.rb3
-rw-r--r--db/fixtures/production/010_settings.rb16
-rw-r--r--db/migrate/20161207231620_fixup_environment_name_uniqueness.rb53
-rw-r--r--db/migrate/20161207231621_create_environment_name_unique_index.rb18
-rw-r--r--db/migrate/20161207231626_add_environment_slug.rb60
-rw-r--r--db/migrate/20161209153400_add_unique_index_for_environment_slug.rb15
-rw-r--r--db/schema.rb16
-rw-r--r--doc/administration/environment_variables.md23
-rw-r--r--doc/administration/logs.md36
-rw-r--r--doc/api/enviroments.md8
-rw-r--r--doc/api/projects.md1
-rw-r--r--doc/ci/environments.md56
-rw-r--r--doc/ci/variables/README.md3
-rw-r--r--doc/ci/yaml/README.md23
-rw-r--r--doc/project_services/img/kubernetes_configuration.pngbin0 -> 113827 bytes
-rw-r--r--doc/project_services/kubernetes.md38
-rw-r--r--doc/project_services/project_services.md1
-rw-r--r--doc/user/markdown.md37
-rw-r--r--features/admin/active_tab.feature54
-rw-r--r--features/steps/admin/active_tab.rb41
-rw-r--r--features/steps/group/milestones.rb2
-rw-r--r--features/steps/shared/authentication.rb2
-rw-r--r--lib/api/entities.rb2
-rw-r--r--lib/api/environments.rb3
-rw-r--r--lib/api/helpers/custom_validators.rb14
-rw-r--r--lib/api/services.rb28
-rw-r--r--lib/banzai/filter/math_filter.rb51
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb1
-rw-r--r--lib/gitlab/asciidoc.rb31
-rw-r--r--lib/gitlab/badge/build/status.rb4
-rw-r--r--lib/gitlab/gfm/uploads_rewriter.rb19
-rw-r--r--lib/gitlab/gon_helper.rb2
-rw-r--r--lib/gitlab/middleware/multipart.rb99
-rw-r--r--lib/gitlab/project_search_results.rb2
-rw-r--r--lib/gitlab/regex.rb17
-rw-r--r--lib/gitlab/sql/union.rb4
-rw-r--r--lib/gitlab/workhorse.rb6
-rw-r--r--lib/rouge/lexers/math.rb21
-rw-r--r--public/404.html9
-rw-r--r--public/422.html9
-rw-r--r--public/500.html9
-rw-r--r--public/502.html9
-rw-r--r--public/503.html9
-rw-r--r--spec/controllers/search_controller_spec.rb61
-rw-r--r--spec/db/production/settings.rb16
-rw-r--r--spec/factories/notes.rb2
-rw-r--r--spec/factories/projects.rb13
-rw-r--r--spec/features/admin/admin_active_tab_spec.rb90
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb8
-rw-r--r--spec/finders/issues_finder_spec.rb71
-rw-r--r--spec/finders/notes_finder_spec.rb200
-rw-r--r--spec/helpers/application_helper_spec.rb14
-rw-r--r--spec/javascripts/abuse_reports_spec.js.es64
-rw-r--r--spec/javascripts/activities_spec.js.es63
-rw-r--r--spec/javascripts/awards_handler_spec.js3
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js2
-rw-r--r--spec/javascripts/boards/boards_store_spec.js.es69
-rw-r--r--spec/javascripts/boards/issue_spec.js.es65
-rw-r--r--spec/javascripts/boards/list_spec.js.es68
-rw-r--r--spec/javascripts/boards/mock_data.js.es63
-rw-r--r--spec/javascripts/dashboard_spec.js.es63
-rw-r--r--spec/javascripts/datetime_utility_spec.js.es62
-rw-r--r--spec/javascripts/diff_comments_store_spec.js.es65
-rw-r--r--spec/javascripts/extensions/object_spec.js.es625
-rw-r--r--spec/javascripts/gl_dropdown_spec.js.es64
-rw-r--r--spec/javascripts/gl_field_errors_spec.js.es63
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_graph_spec.js6
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_util_spec.js4
-rw-r--r--spec/javascripts/graphs/stat_graph_spec.js4
-rw-r--r--spec/javascripts/issue_spec.js5
-rw-r--r--spec/javascripts/labels_issue_sidebar_spec.js.es65
-rw-r--r--spec/javascripts/line_highlighter_spec.js3
-rw-r--r--spec/javascripts/merge_request_spec.js3
-rw-r--r--spec/javascripts/new_branch_spec.js3
-rw-r--r--spec/javascripts/notes_spec.js4
-rw-r--r--spec/javascripts/project_title_spec.js4
-rw-r--r--spec/javascripts/right_sidebar_spec.js3
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js3
-rw-r--r--spec/javascripts/subbable_resource_spec.js.es63
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js4
-rw-r--r--spec/javascripts/u2f/register_spec.js4
-rw-r--r--spec/javascripts/zen_mode_spec.js5
-rw-r--r--spec/lib/banzai/filter/math_filter_spec.rb120
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb4
-rw-r--r--spec/lib/gitlab/badge/build/status_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/avatar_restorer_spec.rb4
-rw-r--r--spec/lib/gitlab/middleware/multipart_spec.rb74
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb29
-rw-r--r--spec/lib/gitlab/regex_spec.rb16
-rw-r--r--spec/lib/gitlab/sql/union_spec.rb22
-rw-r--r--spec/models/build_spec.rb55
-rw-r--r--spec/models/ci/pipeline_spec.rb59
-rw-r--r--spec/models/commit_spec.rb29
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb7
-rw-r--r--spec/models/environment_spec.rb48
-rw-r--r--spec/models/group_spec.rb2
-rw-r--r--spec/models/issue_spec.rb20
-rw-r--r--spec/models/merge_request_spec.rb6
-rw-r--r--spec/models/milestone_spec.rb11
-rw-r--r--spec/models/note_spec.rb38
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb126
-rw-r--r--spec/models/project_services/slack_service/pipeline_message_spec.rb30
-rw-r--r--spec/requests/api/environments_spec.rb17
-rw-r--r--spec/requests/api/groups_spec.rb4
-rw-r--r--spec/serializers/analytics_build_entity_spec.rb8
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb17
-rw-r--r--spec/support/upload_helpers.rb16
-rw-r--r--spec/uploaders/attachment_uploader_spec.rb18
-rw-r--r--spec/uploaders/avatar_uploader_spec.rb18
-rw-r--r--spec/uploaders/file_uploader_spec.rb12
-rw-r--r--vendor/assets/fonts/KaTeX_AMS-Regular.eotbin0 -> 71656 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_AMS-Regular.ttfbin0 -> 71428 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_AMS-Regular.woffbin0 -> 40200 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_AMS-Regular.woff2bin0 -> 33188 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Bold.eotbin0 -> 19836 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Bold.ttfbin0 -> 19588 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Bold.woffbin0 -> 12136 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff2bin0 -> 10604 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Regular.eotbin0 -> 19220 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Regular.ttfbin0 -> 18960 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Regular.woffbin0 -> 11868 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff2bin0 -> 10396 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Bold.eotbin0 -> 36200 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Bold.ttfbin0 -> 35968 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Bold.woffbin0 -> 23388 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Bold.woff2bin0 -> 20476 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Regular.eotbin0 -> 34896 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Regular.ttfbin0 -> 34652 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Regular.woffbin0 -> 22844 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Regular.woff2bin0 -> 19868 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Bold.eotbin0 -> 60688 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Bold.ttfbin0 -> 60468 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Bold.woffbin0 -> 35480 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Bold.woff2bin0 -> 29492 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Italic.eotbin0 -> 44132 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Italic.ttfbin0 -> 43904 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Italic.woffbin0 -> 24880 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Italic.woff2bin0 -> 21032 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Regular.eotbin0 -> 68228 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Regular.ttfbin0 -> 67996 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Regular.woffbin0 -> 37620 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Regular.woff2bin0 -> 31220 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-BoldItalic.eotbin0 -> 39990 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-BoldItalic.ttfbin0 -> 39744 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-BoldItalic.woffbin0 -> 23192 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-BoldItalic.woff2bin0 -> 20036 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Italic.eotbin0 -> 41676 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Italic.ttfbin0 -> 41448 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Italic.woffbin0 -> 23820 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Italic.woff2bin0 -> 20432 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Regular.eotbin0 -> 41536 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Regular.ttfbin0 -> 41304 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Regular.woffbin0 -> 23712 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Regular.woff2bin0 -> 20344 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Bold.eotbin0 -> 34204 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Bold.ttfbin0 -> 33964 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Bold.woffbin0 -> 19196 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Bold.woff2bin0 -> 16020 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Italic.eotbin0 -> 31320 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Italic.ttfbin0 -> 31072 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Italic.woffbin0 -> 18080 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Italic.woff2bin0 -> 15152 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Regular.eotbin0 -> 30212 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Regular.ttfbin0 -> 29960 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Regular.woffbin0 -> 16744 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Regular.woff2bin0 -> 13908 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Script-Regular.eotbin0 -> 25104 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Script-Regular.ttfbin0 -> 24864 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Script-Regular.woffbin0 -> 13856 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Script-Regular.woff2bin0 -> 12276 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size1-Regular.eotbin0 -> 13408 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size1-Regular.ttfbin0 -> 13172 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size1-Regular.woffbin0 -> 6980 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size1-Regular.woff2bin0 -> 5820 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size2-Regular.eotbin0 -> 12648 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size2-Regular.ttfbin0 -> 12412 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size2-Regular.woffbin0 -> 6684 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size2-Regular.woff2bin0 -> 5560 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size3-Regular.eotbin0 -> 8596 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size3-Regular.ttfbin0 -> 8360 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size3-Regular.woffbin0 -> 4776 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size3-Regular.woff2bin0 -> 3856 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size4-Regular.eotbin0 -> 11520 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size4-Regular.ttfbin0 -> 11284 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size4-Regular.woffbin0 -> 6456 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size4-Regular.woff2bin0 -> 5172 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Typewriter-Regular.eotbin0 -> 35784 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Typewriter-Regular.ttfbin0 -> 35528 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Typewriter-Regular.woffbin0 -> 20712 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Typewriter-Regular.woff2bin0 -> 17344 bytes
-rw-r--r--vendor/assets/javascripts/katex.js8642
-rw-r--r--vendor/assets/stylesheets/katex.css934
446 files changed, 12865 insertions, 974 deletions
diff --git a/.eslintignore b/.eslintignore
index 93de4b10dfe..b4bfa5a1f7a 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,5 +1,6 @@
/coverage/
/coverage-javascript/
+/node_modules/
/public/
/tmp/
/vendor/
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 524cb55242b..26aaba0e866 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-1.1.1
+1.2.0
diff --git a/Gemfile b/Gemfile
index 2cc7764e6b8..17e628ceea6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -178,6 +178,9 @@ gem 'asana', '~> 0.4.0'
# FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1'
+# Kubernetes integration
+gem 'kubeclient', '~> 2.2.0'
+
# d3
gem 'd3_rails', '~> 3.5.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 3de1a7cbf26..7269b528e30 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -161,6 +161,8 @@ GEM
diff-lcs (1.2.5)
diffy (3.1.0)
docile (1.1.5)
+ domain_name (0.5.20161021)
+ unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.2.0)
railties (>= 4.2)
dropzonejs-rails (0.7.2)
@@ -318,6 +320,15 @@ GEM
html2text (0.2.0)
nokogiri (~> 1.6)
htmlentities (4.3.4)
+ http (0.9.8)
+ addressable (~> 2.3)
+ http-cookie (~> 1.0)
+ http-form_data (~> 1.0.1)
+ http_parser.rb (~> 0.6.0)
+ http-cookie (1.0.3)
+ domain_name (~> 0.5)
+ http-form_data (1.0.1)
+ http_parser.rb (0.6.0)
httparty (0.13.7)
json (~> 1.8)
multi_xml (>= 0.5.2)
@@ -352,6 +363,10 @@ GEM
knapsack (1.11.0)
rake
timecop (>= 0.1.0)
+ kubeclient (2.2.0)
+ http (= 0.9.8)
+ recursive-open-struct (= 1.0.0)
+ rest-client
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.4.1)
@@ -388,6 +403,7 @@ GEM
mysql2 (0.3.20)
net-ldap (0.12.1)
net-ssh (3.0.1)
+ netrc (0.11.0)
newrelic_rpm (3.16.0.318)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
@@ -543,6 +559,7 @@ GEM
json (~> 1.4)
recaptcha (3.0.0)
json
+ recursive-open-struct (1.0.0)
redcarpet (3.3.3)
redis (3.2.2)
redis-actionpack (5.0.1)
@@ -568,6 +585,10 @@ GEM
listen (~> 3.0)
responders (2.3.0)
railties (>= 4.2.0, < 5.1)
+ rest-client (2.0.0)
+ http-cookie (>= 1.0.2, < 2.0)
+ mime-types (>= 1.16, < 4.0)
+ netrc (~> 0.8)
rinku (2.0.0)
rotp (2.1.2)
rouge (2.0.7)
@@ -859,6 +880,7 @@ DEPENDENCIES
jwt
kaminari (~> 0.17.0)
knapsack (~> 1.11.0)
+ kubeclient (~> 2.2.0)
letter_opener_web (~> 1.3.0)
license_finder (~> 2.1.0)
licensee (~> 8.0.0)
diff --git a/app/assets/javascripts/abuse_reports.js.es6 b/app/assets/javascripts/abuse_reports.js.es6
index 82e526ae0ef..8a260aae1b1 100644
--- a/app/assets/javascripts/abuse_reports.js.es6
+++ b/app/assets/javascripts/abuse_reports.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-param-reassign */
+
((global) => {
const MAX_MESSAGE_LENGTH = 500;
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message';
diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js
index 31852e4750c..5a7d823e84c 100644
--- a/app/assets/javascripts/admin.js
+++ b/app/assets/javascripts/admin.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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, padded-blocks, max-len */
+/* global Turbolinks */
+
(function() {
this.Admin = (function() {
function Admin() {
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 1c625e2f2b1..81c1e01901e 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -1,6 +1,7 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, comma-dangle, prefer-arrow-callback, indent, object-curly-spacing, quote-props, no-param-reassign, padded-blocks, max-len */
+
(function() {
- this.Api = {
+ var Api = {
groupsPath: "/api/:version/groups.json",
groupPath: "/api/:version/groups/:id.json",
namespacesPath: "/api/:version/namespaces.json",
@@ -140,4 +141,5 @@
}
};
+ window.Api = Api;
}).call(this);
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index b7c4673c8e3..043c6a11c4f 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -1,4 +1,11 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len */
+/* global bp */
+/* global Cookies */
+/* global Flash */
+/* global ConfirmDangerModal */
+/* global AwardsHandler */
+/* global Aside */
+
// This is a manifest file that'll be compiled into including all the files listed below.
// 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
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index f4302e2e9f6..107a7978a87 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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-plusplus, no-return-assign, camelcase, padded-blocks */
+/* global Cookies */
+
(function() {
this.AwardsHandler = (function() {
var FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence
diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js
index a5d62f881fe..c62a4c5a456 100644
--- a/app/assets/javascripts/behaviors/autosize.js
+++ b/app/assets/javascripts/behaviors/autosize.js
@@ -1,4 +1,5 @@
-/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, consistent-return, no-undef, padded-blocks, max-len */
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, consistent-return, padded-blocks, max-len */
+/* global autosize */
/*= require jquery.ba-resize */
/*= require autosize */
diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js
index 4edcaa15fe5..586f941a6e3 100644
--- a/app/assets/javascripts/behaviors/quick_submit.js
+++ b/app/assets/javascripts/behaviors/quick_submit.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, camelcase, 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/blob/blob_ci_yaml.js.es6 b/app/assets/javascripts/blob/blob_ci_yaml.js.es6
index 37531aaec9b..57bd13eecf8 100644
--- a/app/assets/javascripts/blob/blob_ci_yaml.js.es6
+++ b/app/assets/javascripts/blob/blob_ci_yaml.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable padded-blocks, no-param-reassign, comma-dangle */
+/* global Api */
+
/*= require blob/template_selector */
((global) => {
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js
index e0a2e8ac12e..eab686c45c3 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, camelcase, object-shorthand, quotes, comma-dangle, prefer-arrow-callback, no-unused-vars, prefer-template, no-useless-escape, no-alert, padded-blocks, max-len */
+/* global Dropzone */
+
(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 7e8f1062ab3..15563e429a0 100644
--- a/app/assets/javascripts/blob/blob_gitignore_selector.js
+++ b/app/assets/javascripts/blob/blob_gitignore_selector.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* 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, padded-blocks */
+/* global Api */
/*= require blob/template_selector */
diff --git a/app/assets/javascripts/blob/blob_gitignore_selectors.js b/app/assets/javascripts/blob/blob_gitignore_selectors.js
index 9a694daa010..d7f95093688 100644
--- a/app/assets/javascripts/blob/blob_gitignore_selectors.js
+++ b/app/assets/javascripts/blob/blob_gitignore_selectors.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-cond-assign, no-sequences, comma-dangle, padded-blocks, max-len */
+/* global BlobGitignoreSelector */
+
(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 9a77fe35d55..d9c6f65a083 100644
--- a/app/assets/javascripts/blob/blob_license_selector.js
+++ b/app/assets/javascripts/blob/blob_license_selector.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* 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, padded-blocks */
+/* global Api */
/*= require blob/template_selector */
diff --git a/app/assets/javascripts/blob/blob_license_selectors.js.es6 b/app/assets/javascripts/blob/blob_license_selectors.js.es6
index adeb8ba1318..268640681d4 100644
--- a/app/assets/javascripts/blob/blob_license_selectors.js.es6
+++ b/app/assets/javascripts/blob/blob_license_selectors.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars, no-param-reassign, padded-blocks */
+/* global BlobLicenseSelector */
+
((global) => {
class BlobLicenseSelectors {
constructor({ $dropdowns, editor }) {
diff --git a/app/assets/javascripts/blob/template_selector.js.es6 b/app/assets/javascripts/blob/template_selector.js.es6
index 0ff5c0fab05..7a1ee9998c8 100644
--- a/app/assets/javascripts/blob/template_selector.js.es6
+++ b/app/assets/javascripts/blob/template_selector.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable indent, comma-dangle, object-shorthand, func-names, space-before-function-paren, arrow-parens, no-unused-vars, class-methods-use-this, no-var, consistent-return, prefer-const, no-param-reassign, space-in-parens, max-len */
+
((global) => {
class TemplateSelector {
constructor({ dropdown, data, pattern, wrapper, editor, fileEndpoint, $input } = {}) {
diff --git a/app/assets/javascripts/blob_edit/blob_edit_bundle.js b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
index b8eb0f60a8e..8c40e36a80a 100644
--- a/app/assets/javascripts/blob_edit/blob_edit_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
@@ -1,4 +1,7 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, vars-on-top, no-unused-vars, no-new, padded-blocks, max-len */
+/* global EditBlob */
+/* global NewCommitForm */
+
/*= require_tree . */
(function() {
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index 0c74aaaa852..b528e32340d 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -1,4 +1,7 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, camelcase, no-param-reassign, 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 */
+/* global ace */
+/* global BlobGitignoreSelectors */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -57,7 +60,7 @@
content: this.editor.getValue()
}, function(response) {
currentPane.empty().append(response);
- return currentPane.syntaxHighlight();
+ return currentPane.renderGFM();
});
} else {
this.$toggleButton.show();
diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6
index 7ba918a05f8..ab2343c72fc 100644
--- a/app/assets/javascripts/boards/boards_bundle.js.es6
+++ b/app/assets/javascripts/boards/boards_bundle.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable one-var, indent, quote-props, comma-dangle, space-before-function-paren */
+/* global Vue */
+/* global BoardService */
+
//= require vue
//= require vue-resource
//= require Sortable
diff --git a/app/assets/javascripts/boards/components/board.js.es6 b/app/assets/javascripts/boards/components/board.js.es6
index 31de3b25284..d1fb0ec48e0 100644
--- a/app/assets/javascripts/boards/components/board.js.es6
+++ b/app/assets/javascripts/boards/components/board.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, space-before-function-paren, one-var, indent, radix */
+/* global Vue */
+/* global Sortable */
+
//= require ./board_blank_state
//= require ./board_delete
//= require ./board_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 691487b272a..0a47a22fad2 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.js.es6
+++ b/app/assets/javascripts/boards/components/board_blank_state.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable space-before-function-paren, comma-dangle, semi */
+/* global Vue */
+/* global ListLabel */
+
(() => {
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6
index 2299dafd217..5fc50280811 100644
--- a/app/assets/javascripts/boards/components/board_card.js.es6
+++ b/app/assets/javascripts/boards/components/board_card.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, space-before-function-paren, dot-notation */
+/* global Vue */
+
(() => {
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/boards/components/board_delete.js.es6 b/app/assets/javascripts/boards/components/board_delete.js.es6
index c45e1926c5c..861600424a5 100644
--- a/app/assets/javascripts/boards/components/board_delete.js.es6
+++ b/app/assets/javascripts/boards/components/board_delete.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, space-before-function-paren, no-alert */
+/* global Vue */
+
(() => {
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 43ebeef39c4..6711930622b 100644
--- a/app/assets/javascripts/boards/components/board_list.js.es6
+++ b/app/assets/javascripts/boards/components/board_list.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, space-before-function-paren, max-len, no-plusplus */
+/* global Vue */
+/* global Sortable */
+
//= require ./board_card
//= require ./board_new_issue
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js.es6 b/app/assets/javascripts/boards/components/board_new_issue.js.es6
index a7989a2ff4c..2386d3a613c 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.js.es6
+++ b/app/assets/javascripts/boards/components/board_new_issue.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, no-unused-vars */
+/* global Vue */
+/* global ListIssue */
+
(() => {
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6
index 1644a772737..02459722bbf 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js.es6
+++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6
@@ -1,4 +1,10 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, space-before-function-paren, no-new */
+/* global Vue */
+/* global IssuableContext */
+/* global MilestoneSelect */
+/* global LabelsSelect */
+/* global Sidebar */
+
(() => {
const Store = gl.issueBoards.BoardsStore;
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 10ce746deb5..3f5cf8420a8 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js.es6
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var, indent */
+
(() => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
@@ -45,10 +46,10 @@
return $li.append($a.prepend($labelColor));
},
- search: {
- fields: ['title']
- },
- filterable: true,
+ search: {
+ fields: ['title']
+ },
+ filterable: true,
selectable: true,
multiSelect: true,
clicked (label, $el, e) {
diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 b/app/assets/javascripts/boards/filters/due_date_filters.js.es6
index 9eceac4eddd..7e192e90fe6 100644
--- a/app/assets/javascripts/boards/filters/due_date_filters.js.es6
+++ b/app/assets/javascripts/boards/filters/due_date_filters.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* global Vue */
+
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 5f99de39122..a5e62ed775d 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars, no-mixed-operators, prefer-const, comma-dangle, semi */
+/* global DocumentTouch */
+
((w) => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6
index 21d735e8231..1199e022ff1 100644
--- a/app/assets/javascripts/boards/models/issue.js.es6
+++ b/app/assets/javascripts/boards/models/issue.js.es6
@@ -1,4 +1,9 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars, space-before-function-paren, arrow-body-style, space-in-parens, arrow-parens, comma-dangle, max-len */
+/* global Vue */
+/* global ListLabel */
+/* global ListMilestone */
+/* global ListUser */
+
class ListIssue {
constructor (obj) {
this.id = obj.iid;
diff --git a/app/assets/javascripts/boards/models/label.js.es6 b/app/assets/javascripts/boards/models/label.js.es6
index 0910fe9a854..8f20a1bbec7 100644
--- a/app/assets/javascripts/boards/models/label.js.es6
+++ b/app/assets/javascripts/boards/models/label.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars, space-before-function-paren */
+
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 429bd27c3fb..a8d38c16485 100644
--- a/app/assets/javascripts/boards/models/list.js.es6
+++ b/app/assets/javascripts/boards/models/list.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable space-before-function-paren, no-underscore-dangle, class-methods-use-this, consistent-return, no-plusplus, prefer-const, space-in-parens, no-shadow, no-param-reassign, max-len, no-unused-vars */
+/* global ListIssue */
+/* global ListLabel */
+
class List {
constructor (obj) {
this.id = obj.id;
diff --git a/app/assets/javascripts/boards/models/milestone.js.es6 b/app/assets/javascripts/boards/models/milestone.js.es6
index a48969e19c9..9c173c1b70b 100644
--- a/app/assets/javascripts/boards/models/milestone.js.es6
+++ b/app/assets/javascripts/boards/models/milestone.js.es6
@@ -1,6 +1,7 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars */
+
class ListMilestone {
- constructor (obj) {
+ 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 583a973fc46..a8a3892e2ad 100644
--- a/app/assets/javascripts/boards/models/user.js.es6
+++ b/app/assets/javascripts/boards/models/user.js.es6
@@ -1,6 +1,7 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars */
+
class ListUser {
- constructor (user) {
+ constructor(user) {
this.id = user.id;
this.name = user.name;
this.username = user.username;
diff --git a/app/assets/javascripts/boards/services/board_service.js.es6 b/app/assets/javascripts/boards/services/board_service.js.es6
index f59a2ed7937..189a8703198 100644
--- a/app/assets/javascripts/boards/services/board_service.js.es6
+++ b/app/assets/javascripts/boards/services/board_service.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable space-before-function-paren, comma-dangle, no-param-reassign, camelcase, prefer-const, no-extra-semi, max-len, no-unused-vars */
+/* global Vue */
+
class BoardService {
constructor (root, boardId) {
this.lists = Vue.resource(`${root}/${boardId}/lists{/id}`, {}, {
diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6
index bb2a4de8b18..e7a14ea5bca 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js.es6
+++ b/app/assets/javascripts/boards/stores/boards_store.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, space-before-function-paren, one-var, indent, space-in-parens, no-shadow, radix, dot-notation, semi, max-len */
+/* global Cookies */
+/* global List */
+
(() => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
diff --git a/app/assets/javascripts/boards/vue_resource_interceptor.js.es6 b/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
index 80f137ca12e..3723a2039f9 100644
--- a/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
+++ b/app/assets/javascripts/boards/vue_resource_interceptor.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars, no-plusplus */
+/* global Vue */
+
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 e7ceb602601..a7e72430141 100644
--- a/app/assets/javascripts/breakpoints.js
+++ b/app/assets/javascripts/breakpoints.js
@@ -1,6 +1,7 @@
-/* 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 */
+/* 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, max-len */
+
(function() {
- this.Breakpoints = (function() {
+ var Breakpoints = (function() {
var BreakpointInstance, instance;
function Breakpoints() {}
@@ -68,4 +69,5 @@
};
})(this));
+ window.Breakpoints = Breakpoints;
}).call(this);
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 116a47b0907..824febe3fd3 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -1,4 +1,7 @@
-/* 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 */
+/* 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, 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 */
+/* global Breakpoints */
+/* global Turbolinks */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -193,6 +196,7 @@
};
Build.prototype.stepTrace = function(e) {
+ var $currentTarget;
e.preventDefault();
$currentTarget = $(e.currentTarget);
$.scrollTo($currentTarget.attr('href'), {
diff --git a/app/assets/javascripts/build_variables.js.es6 b/app/assets/javascripts/build_variables.js.es6
index 0ecd20bc11e..993424d422f 100644
--- a/app/assets/javascripts/build_variables.js.es6
+++ b/app/assets/javascripts/build_variables.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable func-names, prefer-arrow-callback, space-before-blocks, space-before-function-paren, comma-spacing, max-len */
+
$(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 67509ea7d91..67b33a4d7ee 100644
--- a/app/assets/javascripts/commit.js
+++ b/app/assets/javascripts/commit.js
@@ -1,4 +1,6 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-undef, padded-blocks */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, padded-blocks */
+/* global CommitFile */
+
(function() {
this.Commit = (function() {
function Commit() {
diff --git a/app/assets/javascripts/commit/file.js b/app/assets/javascripts/commit/file.js
index 600bac8834a..27512312c7c 100644
--- a/app/assets/javascripts/commit/file.js
+++ b/app/assets/javascripts/commit/file.js
@@ -1,4 +1,6 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, no-undef, padded-blocks, max-len */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, padded-blocks */
+/* global ImageFile */
+
(function() {
this.CommitFile = (function() {
function CommitFile(file) {
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 3627aaf5080..24a6e4ff0e9 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -1,4 +1,6 @@
-/* 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, prefer-arrow-callback */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, 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, prefer-arrow-callback */
+/* global Pager */
+
(function() {
this.CommitsList = (function() {
function CommitsList() {}
@@ -6,8 +8,8 @@
CommitsList.timer = null;
CommitsList.init = function(limit) {
- $("body").on("click", ".day-commits-table li.commit", function(event) {
- if (event.target.nodeName !== "A") {
+ $("body").on("click", ".day-commits-table li.commit", function(e) {
+ if (e.target.nodeName !== "A") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
diff --git a/app/assets/javascripts/compare_autocomplete.js.es6 b/app/assets/javascripts/compare_autocomplete.js.es6
index bd980f87e72..45c974b2b68 100644
--- a/app/assets/javascripts/compare_autocomplete.js.es6
+++ b/app/assets/javascripts/compare_autocomplete.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, no-dupe-keys, wrap-iife, padded-blocks, max-len */
+
(function() {
this.CompareAutocomplete = (function() {
function CompareAutocomplete() {
diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js
index efa228a75d9..6a13f38588d 100644
--- a/app/assets/javascripts/copy_to_clipboard.js
+++ b/app/assets/javascripts/copy_to_clipboard.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, padded-blocks, max-len */
+/* global Clipboard */
/*= require clipboard */
diff --git a/app/assets/javascripts/create_label.js.es6 b/app/assets/javascripts/create_label.js.es6
index 744aa0afa03..947c129d5b5 100644
--- a/app/assets/javascripts/create_label.js.es6
+++ b/app/assets/javascripts/create_label.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-param-reassign, wrap-iife, max-len */
+/* global Api */
+
(function (w) {
class CreateLabelDropdown {
constructor ($el, namespacePath, projectPath) {
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 52e2846d279..c59d3996fab 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,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, semi, max-len */
+/* global Vue */
+/* global CommentsStore */
+
(() => {
const CommentAndResolveBtn = Vue.extend({
props: {
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 983e554b9c1..f47867fc3b0 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,6 +1,10 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, one-var, indent, space-before-function-paren, no-plusplus, no-lonely-if, no-continue, brace-style, max-len, quotes, semi */
+/* global Vue */
+/* global DiscussionMixins */
+/* global CommentsStore */
+
(() => {
- JumpToDiscussion = Vue.extend({
+ const JumpToDiscussion = Vue.extend({
mixins: [DiscussionMixins],
props: {
discussionId: String
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 27af9fc96ad..88a19fc6e1d 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
@@ -1,4 +1,9 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */
+/* global Vue */
+/* global CommentsStore */
+/* global ResolveService */
+/* global Flash */
+
(() => {
const ResolveBtn = Vue.extend({
props: {
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 9522ccb49da..72cdae812bc 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_count.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_count.js.es6
@@ -1,4 +1,8 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, object-shorthand, func-names, no-param-reassign */
+/* global Vue */
+/* global DiscussionMixins */
+/* global CommentsStore */
+
((w) => {
w.ResolveCount = Vue.extend({
mixins: [DiscussionMixins],
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 b945a09fcbe..ee5f62b2d9e 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,4 +1,8 @@
-/* eslint-disable */
+/* eslint-disable object-shorthand, func-names, space-before-function-paren, comma-dangle, no-else-return, quotes, max-len */
+/* global Vue */
+/* global CommentsStore */
+/* global ResolveService */
+
(() => {
const ResolveDiscussionBtn = Vue.extend({
props: {
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 bd4c20aed8b..840b5aa922e 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable func-names, comma-dangle, new-cap, no-new */
+/* global Vue */
+/* global ResolveCount */
+
//= require vue
//= require vue-resource
//= require_directory ./models
diff --git a/app/assets/javascripts/diff_notes/mixins/discussion.js.es6 b/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
index 7a929017f36..a9ea18bf82b 100644
--- a/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/mixins/discussion.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable object-shorthand, func-names, guard-for-in, no-restricted-syntax, comma-dangle, no-plusplus, no-param-reassign, max-len */
+
((w) => {
w.DiscussionMixins = {
computed: {
diff --git a/app/assets/javascripts/diff_notes/models/discussion.js.es6 b/app/assets/javascripts/diff_notes/models/discussion.js.es6
index badcdccc840..efcd46680a7 100644
--- a/app/assets/javascripts/diff_notes/models/discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/models/discussion.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable space-before-function-paren, camelcase, guard-for-in, no-restricted-syntax, no-unused-vars, max-len */
+/* global Vue */
+/* global NoteModel */
+
class DiscussionModel {
constructor (discussionId) {
this.id = discussionId;
@@ -69,7 +72,7 @@ class DiscussionModel {
gl.utils.localTimeAgo($('.js-timeago', `${discussionSelector}`));
} else {
- $discussionHeadline.remove();
+ $discussionHeadline.remove();
}
}
diff --git a/app/assets/javascripts/diff_notes/models/note.js.es6 b/app/assets/javascripts/diff_notes/models/note.js.es6
index d0541b02632..e3bce1d2038 100644
--- a/app/assets/javascripts/diff_notes/models/note.js.es6
+++ b/app/assets/javascripts/diff_notes/models/note.js.es6
@@ -1,6 +1,7 @@
-/* eslint-disable */
+/* eslint-disable camelcase, no-unused-vars */
+
class NoteModel {
- constructor (discussionId, noteId, canResolve, resolved, resolved_by) {
+ constructor(discussionId, noteId, canResolve, resolved, resolved_by) {
this.discussionId = discussionId;
this.id = noteId;
this.canResolve = canResolve;
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js.es6 b/app/assets/javascripts/diff_notes/services/resolve.js.es6
index 86953ce7ffb..78c74985f78 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js.es6
+++ b/app/assets/javascripts/diff_notes/services/resolve.js.es6
@@ -1,4 +1,8 @@
-/* eslint-disable */
+/* eslint-disable class-methods-use-this, one-var, indent, camelcase, no-new, comma-dangle, semi, no-param-reassign, max-len */
+/* global Vue */
+/* global Flash */
+/* global CommentsStore */
+
((w) => {
class ResolveServiceClass {
constructor() {
diff --git a/app/assets/javascripts/diff_notes/stores/comments.js.es6 b/app/assets/javascripts/diff_notes/stores/comments.js.es6
index f42ca406bb1..1a7abbc6f75 100644
--- a/app/assets/javascripts/diff_notes/stores/comments.js.es6
+++ b/app/assets/javascripts/diff_notes/stores/comments.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable object-shorthand, func-names, camelcase, prefer-const, no-restricted-syntax, guard-for-in, comma-dangle, max-len, no-param-reassign */
+/* global Vue */
+/* global DiscussionModel */
+
((w) => {
w.CommentsStore = {
state: {},
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 413117c2226..7d588e8eee6 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -1,4 +1,42 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len, padded-blocks */
+/* global UsernameValidator */
+/* global ActiveTabMemoizer */
+/* global ShortcutsNavigation */
+/* global Build */
+/* global Issuable */
+/* global Issue */
+/* global ShortcutsIssuable */
+/* global ZenMode */
+/* global Milestone */
+/* global GLForm */
+/* global IssuableForm */
+/* global LabelsSelect */
+/* global MilestoneSelect */
+/* global MergedButtons */
+/* global Commit */
+/* global NotificationsForm */
+/* global TreeView */
+/* global NotificationsDropdown */
+/* global UsersSelect */
+/* global GroupAvatar */
+/* global LineHighlighter */
+/* global ShortcutsBlob */
+/* global ProjectFork */
+/* global BuildArtifacts */
+/* global GroupsSelect */
+/* global Search */
+/* global Admin */
+/* global NamespaceSelects */
+/* global ShortcutsDashboardNavigation */
+/* global Project */
+/* global ProjectAvatar */
+/* global CompareAutocomplete */
+/* global ProjectNew */
+/* global Star */
+/* global ProjectShow */
+/* global Labels */
+/* global Shortcuts */
+
(function() {
var Dispatcher;
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index e1e76bca6ad..56cb39be642 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* 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, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, no-plusplus, prefer-arrow-callback, padded-blocks */
+/* global Dropzone */
/*= require preview_markdown */
diff --git a/app/assets/javascripts/due_date_select.js.es6 b/app/assets/javascripts/due_date_select.js.es6
index e84f5ac9183..2b7d57d86c6 100644
--- a/app/assets/javascripts/due_date_select.js.es6
+++ b/app/assets/javascripts/due_date_select.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable wrap-iife, func-names, space-before-function-paren, comma-dangle, prefer-template, consistent-return, class-methods-use-this, arrow-body-style, prefer-const, padded-blocks, no-unused-vars, no-underscore-dangle, no-new, max-len, semi, no-sequences, no-unused-expressions, no-param-reassign */
+
(function(global) {
class DueDateSelect {
constructor({ $dropdown, $loading } = {}) {
diff --git a/app/assets/javascripts/extensions/object.js.es6 b/app/assets/javascripts/extensions/object.js.es6
new file mode 100644
index 00000000000..70a2d765abd
--- /dev/null
+++ b/app/assets/javascripts/extensions/object.js.es6
@@ -0,0 +1,26 @@
+/* eslint-disable no-restricted-syntax */
+
+// Adapted from https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill
+if (typeof Object.assign !== 'function') {
+ Object.assign = function assign(target, ...args) {
+ if (target == null) { // TypeError if undefined or null
+ throw new TypeError('Cannot convert undefined or null to object');
+ }
+
+ const to = Object(target);
+
+ for (let index = 0; index < args.length; index += 1) {
+ const nextSource = args[index];
+
+ if (nextSource != null) { // Skip over if undefined or null
+ for (const nextKey in nextSource) {
+ // Avoid bugs when hasOwnProperty is shadowed
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
+ to[nextKey] = nextSource[nextKey];
+ }
+ }
+ }
+ }
+ return to;
+ };
+}
diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js
index 0122e847161..785f2869970 100644
--- a/app/assets/javascripts/files_comment_button.js
+++ b/app/assets/javascripts/files_comment_button.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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 */
+/* global FilesCommentButton */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index 2f3da745119..90010b9fd54 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, no-template-curly-in-string, comma-dangle, object-shorthand, quotes, dot-notation, no-else-return, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-param-reassign, no-useless-escape, prefer-template, consistent-return, wrap-iife, prefer-arrow-callback, camelcase, no-unused-vars, no-useless-return, padded-blocks, vars-on-top, indent, no-extra-semi, no-multi-spaces, semi, max-len */
+
// Creates the variables for setting up GFM auto-completion
(function() {
if (window.GitLab == null) {
@@ -9,7 +10,7 @@
return str.replace(/<(?:.|\n)*?>/gm, '');
}
- GitLab.GfmAutoComplete = {
+ window.GitLab.GfmAutoComplete = {
dataLoading: false,
dataLoaded: false,
cachedData: {},
@@ -56,7 +57,7 @@
var withoutAt = value.substring(1);
if (withoutAt && /[^\w\d]/.test(withoutAt)) value = value.charAt() + '"' + withoutAt + '"';
}
- if (!GitLab.GfmAutoComplete.dataLoaded) {
+ if (!window.GitLab.GfmAutoComplete.dataLoaded) {
return this.at;
} else {
return value;
@@ -66,14 +67,15 @@
// The below is taken from At.js source
// Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js
- var _a, _y, regexp, match;
+ var _a, _y, regexp, match, atSymbols;
+ atSymbols = Object.keys(this.app.controllers).join('|');
subtext = subtext.split(' ').pop();
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
_a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF");
- regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "(?!\\W)([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)|([^\\x00-\\xff]*)$", 'gi');
+ regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "(?![" + atSymbols + "])([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)$", 'gi');
match = regexp.exec(subtext);
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 9a91018a8e4..68a345d83f9 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,4 +1,7 @@
-/* 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 */
+/* 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, 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 */
+/* global fuzzaldrinPlus */
+/* global Turbolinks */
+
(function() {
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
@@ -188,7 +191,7 @@
})();
GitLabDropdown = (function() {
- var ACTIVE_CLASS, FILTER_INPUT, INDETERMINATE_CLASS, LOADING_CLASS, PAGE_TWO_CLASS, NON_SELECTABLE_CLASSES, SELECTABLE_CLASSES, currentIndex;
+ var ACTIVE_CLASS, FILTER_INPUT, INDETERMINATE_CLASS, LOADING_CLASS, PAGE_TWO_CLASS, NON_SELECTABLE_CLASSES, SELECTABLE_CLASSES, CURSOR_SELECT_SCROLL_PADDING, currentIndex;
LOADING_CLASS = "is-loading";
diff --git a/app/assets/javascripts/gl_field_errors.js.es6 b/app/assets/javascripts/gl_field_errors.js.es6
index 6ce392d2a5b..63f9cafa8d0 100644
--- a/app/assets/javascripts/gl_field_errors.js.es6
+++ b/app/assets/javascripts/gl_field_errors.js.es6
@@ -1,4 +1,4 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, class-methods-use-this, max-len, space-before-function-paren, arrow-parens, no-param-reassign, padded-blocks */
//= require gl_field_error
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index db5d9e75b3a..56a33eeaad5 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -1,4 +1,8 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-undef, no-new, padded-blocks, max-len */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-new, padded-blocks, max-len */
+/* global GitLab */
+/* global DropzoneInput */
+/* global autosize */
+
(function() {
this.GLForm = (function() {
function GLForm(form) {
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/graphs/stat_graph_contributors.js
index c3a132b3c75..2d08a7c6ac3 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors.js
@@ -1,4 +1,9 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, padded-blocks */
+/* global ContributorsGraph */
+/* global ContributorsAuthorGraph */
+/* global ContributorsMasterGraph */
+/* global ContributorsStatGraphUtil */
+/* global d3 */
/*= 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 cb2448e8cc7..9c5e9381e52 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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, newline-per-chained-call, no-else-return */
+/* global d3 */
+/* global ContributorsGraph */
/*= require d3 */
diff --git a/app/assets/javascripts/group_label_subscription.js.es6 b/app/assets/javascripts/group_label_subscription.js.es6
index eea6cd40859..8e10e424412 100644
--- a/app/assets/javascripts/group_label_subscription.js.es6
+++ b/app/assets/javascripts/group_label_subscription.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable func-names, object-shorthand, comma-dangle, wrap-iife, space-before-function-paren, no-param-reassign, padded-blocks, max-len */
+
(function(global) {
class GroupLabelSubscription {
constructor(container) {
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index 3dc6f05ca20..99700e7562a 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, one-var, camelcase, one-var-declaration-per-line, quotes, object-shorthand, prefer-arrow-callback, comma-dangle, consistent-return, yoda, prefer-rest-params, prefer-spread, no-unused-vars, prefer-template, padded-blocks, max-len */
+/* global Api */
+
(function() {
var slice = [].slice;
@@ -14,7 +16,7 @@
multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0,
query: function(query) {
- options = { all_available: all_available, skip_groups: skip_groups };
+ var options = { all_available: all_available, skip_groups: skip_groups };
return Api.groups(query.term, options, function(groups) {
var data;
data = {
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
index 9425b6ed9d4..fa795be07ed 100644
--- a/app/assets/javascripts/importer_status.js
+++ b/app/assets/javascripts/importer_status.js
@@ -1,6 +1,7 @@
-/* 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 */
+/* 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, max-len */
+
(function() {
- this.ImporterStatus = (function() {
+ window.ImporterStatus = (function() {
function ImporterStatus(jobs_url, import_url) {
this.jobs_url = jobs_url;
this.import_url = import_url;
@@ -75,7 +76,7 @@
var jobsImportPath = $('.js-importer-status').data('jobs-import-path');
var importPath = $('.js-importer-status').data('import-path');
- new ImporterStatus(jobsImportPath, importPath);
+ new window.ImporterStatus(jobsImportPath, importPath);
}
});
}).call(this);
diff --git a/app/assets/javascripts/issuable.js.es6 b/app/assets/javascripts/issuable.js.es6
index 46503c290ae..b174eb2ff96 100644
--- a/app/assets/javascripts/issuable.js.es6
+++ b/app/assets/javascripts/issuable.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, prefer-const, padded-blocks, wrap-iife, max-len */
+/* global Issuable */
+/* global Turbolinks */
+
(function() {
var issuable_created;
diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js
index 317818951fd..4aaad111082 100644
--- a/app/assets/javascripts/issuable_context.js
+++ b/app/assets/javascripts/issuable_context.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, padded-blocks, max-len */
+/* global UsersSelect */
+
(function() {
this.IssuableContext = (function() {
function IssuableContext(currentUser) {
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index 50fdbc89c7c..2f3cad13cc0 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -1,4 +1,9 @@
-/* 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 */
+/* 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-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 */
+/* global GitLab */
+/* global UsersSelect */
+/* global ZenMode */
+/* global Autosave */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 8540b199aba..63b70d4be17 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* 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-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, padded-blocks, max-len */
+/* global Flash */
/*= require flash */
/*= require jquery.waitforimages */
diff --git a/app/assets/javascripts/issues_bulk_assignment.js.es6 b/app/assets/javascripts/issues_bulk_assignment.js.es6
index 9697fb33566..ad25104152c 100644
--- a/app/assets/javascripts/issues_bulk_assignment.js.es6
+++ b/app/assets/javascripts/issues_bulk_assignment.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, radix, max-len, padded-blocks, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */
+/* global Issuable */
+/* global Flash */
+
((global) => {
class IssuableBulkActions {
diff --git a/app/assets/javascripts/label_manager.js.es6 b/app/assets/javascripts/label_manager.js.es6
index 175623e7448..33c5e35324d 100644
--- a/app/assets/javascripts/label_manager.js.es6
+++ b/app/assets/javascripts/label_manager.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, consistent-return, func-names, space-before-function-paren, padded-blocks, max-len */
+/* global Flash */
+
((global) => {
class LabelManager {
@@ -104,4 +106,3 @@
gl.LabelManager = LabelManager;
})(window.gl || (window.gl = {}));
-
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index f334f35594d..d29a5edb9ad 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,4 +1,7 @@
-/* eslint-disable no-useless-return, 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 */
+/* eslint-disable no-useless-return, 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, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks */
+/* global Issuable */
+/* global ListLabel */
+
(function() {
this.LabelsSelect = (function() {
function LabelsSelect() {
diff --git a/app/assets/javascripts/lib/ace.js b/app/assets/javascripts/lib/ace.js
index b1718e89d3d..4cdf99cae72 100644
--- a/app/assets/javascripts/lib/ace.js
+++ b/app/assets/javascripts/lib/ace.js
@@ -1,3 +1,2 @@
-/* eslint-disable */
/*= require ace-rails-ap */
/*= require ace/ext-searchbox */
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index d0fe69260a5..3c9ad0e67c8 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, consistent-return, 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/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index b0f834705c3..9af89b79f84 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* 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, 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 */
+
// LineHighlighter
//
// Handles single- and multi-line selection and highlight for blob views.
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
index 9e4ffd07dbd..f95b079c972 100644
--- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
+++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
@@ -1,4 +1,8 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, quote-props, no-useless-computed-key, object-shorthand, prefer-const, no-new, padded-blocks, no-param-reassign, semi, max-len */
+/* global Vue */
+/* global ace */
+/* global Flash */
+
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
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
index 23c4618af70..74544b7d0c7 100644
--- a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6
+++ b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable padded-blocks, no-param-reassign, comma-dangle */
+/* global Vue */
+
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
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
index 4ccbdcd6daa..78c00c31c16 100644
--- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable padded-blocks, no-param-reassign, comma-dangle */
+/* global Vue */
+
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
@@ -19,7 +21,7 @@
</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>
+ </template>
</tr>
</table>
`,
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6
index 8a7519b0786..8df3170edac 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-param-reassign, comma-dangle, no-extra-semi, padded-blocks */
+
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6
index f94e51e783c..53b44007510 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, object-shorthand, no-dupe-keys, no-param-reassign, no-plusplus, camelcase, prefer-const, no-nested-ternary, no-continue, semi, func-call-spacing, no-spaced-func, padded-blocks, max-len */
+/* global Cookies */
+/* global Vue */
+
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6
index 815443fb54e..83520702f9b 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable new-cap, comma-dangle, no-new, semi */
+/* global Vue */
+/* global Flash */
+
//= require vue
//= require ./merge_conflict_store
//= require ./merge_conflict_service
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
index c8de586aa21..e89b35d5407 100644
--- a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6
+++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-param-reassign, comma-dangle, padded-blocks */
+
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
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
index 88c3a20ce13..a4aca85d460 100644
--- a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6
+++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-param-reassign, quote-props, comma-dangle, padded-blocks */
+
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index 88c3636be6c..70f9a8d1955 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, padded-blocks, max-len */
+/* global MergeRequestTabs */
/*= require jquery.waitforimages */
/*= require task_list */
diff --git a/app/assets/javascripts/merge_request_widget.js.es6 b/app/assets/javascripts/merge_request_widget.js.es6
index 7022aa1263b..e47047c4cca 100644
--- a/app/assets/javascripts/merge_request_widget.js.es6
+++ b/app/assets/javascripts/merge_request_widget.js.es6
@@ -1,5 +1,10 @@
-/* eslint-disable */
- ((global) => {
+/* eslint-disable max-len, no-var, func-names, space-before-function-paren, vars-on-top, no-plusplus, comma-dangle, no-return-assign, consistent-return, no-param-reassign, one-var, one-var-declaration-per-line, quotes, prefer-template, no-else-return, prefer-arrow-callback, no-unused-vars, no-underscore-dangle, no-shadow, no-mixed-operators, template-curly-spacing, camelcase, default-case, wrap-iife, semi, padded-blocks */
+/* global notify */
+/* global notifyPermissions */
+/* global merge_request_widget */
+/* global Turbolinks */
+
+((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; };
const DEPLOYMENT_TEMPLATE = `<div class="mr-widget-heading" id="<%- id %>">
@@ -27,7 +32,7 @@
</div>
</div>`;
- global.MergeRequestWidget = (function() {
+ global.MergeRequestWidget = (function() {
function MergeRequestWidget(opts) {
// Initialize MergeRequestWidget behavior
//
@@ -82,10 +87,10 @@
};
MergeRequestWidget.prototype.retrieveSuccessIcon = function() {
- const $ciSuccessIcon = $('.js-success-icon');
- this.$ciSuccessIcon = $ciSuccessIcon.html();
- $ciSuccessIcon.remove();
- }
+ const $ciSuccessIcon = $('.js-success-icon');
+ this.$ciSuccessIcon = $ciSuccessIcon.html();
+ $ciSuccessIcon.remove();
+ }
MergeRequestWidget.prototype.mergeInProgress = function(deleteSourceBranch) {
if (deleteSourceBranch == null) {
@@ -204,7 +209,7 @@
const template = _.template(templateString)(environment)
this.$widgetBody.before(template);
}
- };
+ };
MergeRequestWidget.prototype.showCIStatus = function(state) {
var allowed_states;
@@ -246,4 +251,4 @@
})();
- })(window.gl || (window.gl = {}));
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merged_buttons.js b/app/assets/javascripts/merged_buttons.js
index 15a12c3d985..9f8af46c715 100644
--- a/app/assets/javascripts/merged_buttons.js
+++ b/app/assets/javascripts/merged_buttons.js
@@ -1,4 +1,5 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-undef, padded-blocks, max-len */
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, 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 db7561a3a75..42152362e60 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-use-before-define, camelcase, quotes, object-shorthand, no-shadow, no-unused-vars, 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 */
+/* global Flash */
+
(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 67796083790..28054b78249 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -1,4 +1,8 @@
-/* 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 */
+/* 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-param-reassign, no-shadow, padded-blocks */
+/* global Vue */
+/* global Issuable */
+/* global ListMilestone */
+
(function() {
this.MilestoneSelect = (function() {
function MilestoneSelect(currentProject) {
diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js
index 87c903ec576..6633f2c2709 100644
--- a/app/assets/javascripts/namespace_select.js
+++ b/app/assets/javascripts/namespace_select.js
@@ -1,8 +1,10 @@
-/* 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 */
+/* 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, prefer-arrow-callback, padded-blocks, no-param-reassign, no-cond-assign, max-len */
+/* global Api */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
- this.NamespaceSelect = (function() {
+ window.NamespaceSelect = (function() {
function NamespaceSelect(opts) {
this.onSelectItem = bind(this.onSelectItem, this);
var fieldName, showAny;
@@ -64,7 +66,7 @@
})();
- this.NamespaceSelects = (function() {
+ window.NamespaceSelects = (function() {
function NamespaceSelects(opts) {
var ref;
if (opts == null) {
@@ -74,7 +76,7 @@
this.$dropdowns.each(function(i, dropdown) {
var $dropdown;
$dropdown = $(dropdown);
- return new NamespaceSelect({
+ return new window.NamespaceSelect({
dropdown: $dropdown
});
});
diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js
index e3dc599b90a..64b19a54893 100644
--- a/app/assets/javascripts/network/branch_graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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-plusplus, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len, padded-blocks */
+/* global Raphael */
+
(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 5a8f723a27b..2367d2497b2 100644
--- a/app/assets/javascripts/network/network.js
+++ b/app/assets/javascripts/network/network.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, quote-props, prefer-template, comma-dangle, padded-blocks, max-len */
+/* global BranchGraph */
+
(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 732d92845cb..17833d3419a 100644
--- a/app/assets/javascripts/network/network_bundle.js
+++ b/app/assets/javascripts/network/network_bundle.js
@@ -1,4 +1,7 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, quotes, no-var, vars-on-top, camelcase, comma-dangle, consistent-return, padded-blocks, max-len */
+/* global Network */
+/* global ShortcutsNetwork */
+
// This is a manifest file that'll be compiled into including all the files listed below.
// 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
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 0ca0e255595..a8b9a352870 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,4 +1,8 @@
-/* eslint-disable no-restricted-properties, 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 */
+/* eslint-disable no-restricted-properties, 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, 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 */
+/* global Flash */
+/* global GLForm */
+/* global Autosave */
+/* global ResolveService */
/*= require autosave */
/*= require autosize */
@@ -305,7 +309,7 @@
}
row = form.closest("tr");
note_html = $(note.html);
- note_html.syntaxHighlight();
+ note_html.renderGFM();
// is this the first note of discussion?
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']");
if ((note.original_discussion_id != null) && discussionContainer.length === 0) {
@@ -322,7 +326,7 @@
discussionContainer.append(note_html);
// Init discussion on 'Discussion' page if it is merge request page
if ($('body').attr('data-page').indexOf('projects:merge_request') === 0) {
- $('ul.main-notes-list').append(note.discussion_html).syntaxHighlight();
+ $('ul.main-notes-list').append(note.discussion_html).renderGFM();
}
} else {
// append new note to all matching discussions
@@ -463,7 +467,7 @@
// Convert returned HTML to a jQuery object so we can modify it further
$html = $(note.html);
gl.utils.localTimeAgo($('.js-timeago', $html));
- $html.syntaxHighlight();
+ $html.renderGFM();
$html.find('.js-task-list-container').taskList('enable');
// Find the note's `li` element by ID and replace it with the updated HTML
$note_li = $('.note-row-' + note.id);
@@ -671,7 +675,7 @@
*/
Notes.prototype.addDiffNote = function(e) {
- var $link, addForm, hasNotes, lineType, newForm, nextRow, noteForm, notesContent, replyButton, row, rowCssToAdd, targetContent;
+ var $link, addForm, hasNotes, lineType, newForm, nextRow, noteForm, notesContent, notesContentSelector, replyButton, row, rowCssToAdd, targetContent;
e.preventDefault();
$link = $(e.currentTarget);
row = $link.closest("tr");
diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js
index b152d26733f..324b68a7efc 100644
--- a/app/assets/javascripts/notifications_dropdown.js
+++ b/app/assets/javascripts/notifications_dropdown.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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, padded-blocks, max-len */
+/* global Flash */
+
(function() {
this.NotificationsDropdown = (function() {
function NotificationsDropdown() {
diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6
index fb95648e1c7..0b09ad113a3 100644
--- a/app/assets/javascripts/pipelines.js.es6
+++ b/app/assets/javascripts/pipelines.js.es6
@@ -1,6 +1,7 @@
+/* eslint-disable no-new, guard-for-in, no-restricted-syntax, no-continue, padded-blocks, no-param-reassign, max-len */
+
//= require lib/utils/bootstrap_linked_tabs
-/* eslint-disable */
((global) => {
class Pipelines {
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 3723aa24942..1e261cd49c2 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -1,4 +1,5 @@
-/* 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 */
+/* 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, camelcase, prefer-arrow-callback, max-len */
+
// MarkdownPreview
//
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
@@ -7,7 +8,7 @@
(function() {
var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector;
- this.MarkdownPreview = (function() {
+ window.MarkdownPreview = (function() {
function MarkdownPreview() {}
// Minimum number of users referenced before triggering a warning
@@ -27,7 +28,7 @@
return this.renderMarkdown(mdText, (function(_this) {
return function(response) {
preview.html(response.body);
- preview.syntaxHighlight();
+ preview.renderGFM();
return _this.renderReferencedUsers(response.references.users, form);
};
})(this));
@@ -83,7 +84,7 @@
})();
- markdownPreview = new MarkdownPreview();
+ markdownPreview = new window.MarkdownPreview();
previewButtonSelector = '.js-md-preview-button';
diff --git a/app/assets/javascripts/profile/gl_crop.js.es6 b/app/assets/javascripts/profile/gl_crop.js.es6
index 6da6c1d0295..b4b6da41f63 100644
--- a/app/assets/javascripts/profile/gl_crop.js.es6
+++ b/app/assets/javascripts/profile/gl_crop.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-useless-escape, max-len, padded-blocks, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, no-plusplus, new-parens, semi */
+
((global) => {
// Matches everything but the file name
diff --git a/app/assets/javascripts/profile/profile.js.es6 b/app/assets/javascripts/profile/profile.js.es6
index 3eb81808bd6..eb2fe3477a2 100644
--- a/app/assets/javascripts/profile/profile.js.es6
+++ b/app/assets/javascripts/profile/profile.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, no-unused-vars, class-methods-use-this, quotes, consistent-return, func-names, prefer-arrow-callback, space-before-function-paren, max-len, padded-blocks */
+/* global Flash */
+
((global) => {
class Profile {
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index 016d999d77e..fcf3a4af956 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -1,4 +1,8 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, consistent-return, 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 */
+/* global Cookies */
+/* global Turbolinks */
+/* global ProjectSelect */
+
(function() {
this.Project = (function() {
function Project() {
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 804306a3293..1bd232314d0 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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, object-shorthand, no-param-reassign, comma-dangle, no-plusplus, prefer-template, no-unused-vars, no-return-assign, padded-blocks */
+/* global fuzzaldrinPlus */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js
index c99e55234cf..02dafcfb865 100644
--- a/app/assets/javascripts/project_import.js
+++ b/app/assets/javascripts/project_import.js
@@ -1,4 +1,6 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-undef, padded-blocks, max-len */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, padded-blocks, max-len */
+/* global Turbolinks */
+
(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
index 03a115cb35b..b8d6a198996 100644
--- a/app/assets/javascripts/project_label_subscription.js.es6
+++ b/app/assets/javascripts/project_label_subscription.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable wrap-iife, func-names, space-before-function-paren, object-shorthand, comma-dangle, one-var, one-var-declaration-per-line, no-restricted-syntax, prefer-const, max-len, no-param-reassign, padded-blocks */
+
(function(global) {
class ProjectLabelSubscription {
constructor(container) {
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index fe1f96872f3..650996700ba 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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-else-return, quotes, padded-blocks, max-len */
+/* global Api */
+
(function() {
this.ProjectSelect = (function() {
function ProjectSelect() {
diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js
index dbf530bed41..4548dc68fe1 100644
--- a/app/assets/javascripts/projects_list.js
+++ b/app/assets/javascripts/projects_list.js
@@ -1,6 +1,7 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-var, one-var, one-var-declaration-per-line, prefer-arrow-callback, consistent-return, no-unused-vars, camelcase, prefer-template, comma-dangle, padded-blocks, max-len */
+
(function() {
- this.ProjectsList = {
+ window.ProjectsList = {
init: function() {
$(".projects-list-filter").off('keyup');
this.initSearch();
@@ -9,7 +10,7 @@
initSearch: function() {
var debounceFilter, projectsListFilter;
projectsListFilter = $('.projects-list-filter');
- debounceFilter = _.debounce(ProjectsList.filterResults, 500);
+ debounceFilter = _.debounce(window.ProjectsList.filterResults, 500);
return projectsListFilter.on('keyup', function(e) {
if (projectsListFilter.val() !== '') {
return debounceFilter();
diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6
index 2d60947a666..4aef1c84b56 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable arrow-parens, no-param-reassign, no-irregular-whitespace, object-shorthand, no-else-return, comma-dangle, semi, padded-blocks, max-len */
+
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_create.js.es6
index c45c9d8ff22..f26fba979a4 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_create.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_create.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable no-new, arrow-parens, no-param-reassign, no-irregular-whitespace, comma-dangle, padded-blocks, semi, max-len */
+/* global ProtectedBranchDropdown */
+
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
index e3f226e9a2a..08264ad3d2f 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, no-unused-vars */
+
class ProtectedBranchDropdown {
constructor(options) {
this.onSelect = options.onSelect;
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6
index ac3142ffb07..4ff2fa5a80f 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable no-new, arrow-parens, no-param-reassign, no-irregular-whitespace, padded-blocks, comma-dangle, no-trailing-spaces, semi, max-len */
+/* global Flash */
+
(global => {
global.gl = global.gl || {};
@@ -33,7 +35,7 @@
const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
// Do not update if one dropdown has not selected any option
- if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
+ if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
$.ajax({
type: 'POST',
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6
index 705378a364d..b6972ef2e16 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable arrow-parens, no-param-reassign, no-irregular-whitespace, no-new, comma-dangle, semi, padded-blocks, max-len */
+
(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
index 17e34163831..15b3affd469 100644
--- a/app/assets/javascripts/protected_branches/protected_branches_bundle.js
+++ b/app/assets/javascripts/protected_branches/protected_branches_bundle.js
@@ -1,2 +1 @@
-/* eslint-disable */
/*= require_tree . */
diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js
new file mode 100644
index 00000000000..bbb2f186655
--- /dev/null
+++ b/app/assets/javascripts/render_gfm.js
@@ -0,0 +1,16 @@
+/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
+// Render Gitlab flavoured Markdown
+//
+// Delegates to syntax highlight and render math
+//
+(function() {
+ $.fn.renderGFM = function() {
+ this.find('.js-syntax-highlight').syntaxHighlight();
+ this.find('.js-render-math').renderMath();
+ };
+
+ $(document).on('ready page:load', function() {
+ return $('body').renderGFM();
+ });
+
+}).call(this);
diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/render_math.js
new file mode 100644
index 00000000000..a8a56430f88
--- /dev/null
+++ b/app/assets/javascripts/render_math.js
@@ -0,0 +1,55 @@
+/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
+// Renders math using KaTeX in any element with the
+// `js-render-math` class
+//
+// ### Example Markup
+//
+// <code class="js-render-math"></div>
+//
+(function() {
+ // Only load once
+ var katexLoaded = false;
+
+ // Loop over all math elements and render math
+ var renderWithKaTeX = function (elements) {
+ elements.each(function () {
+ var mathNode = $('<span></span>');
+ var $this = $(this);
+
+ var display = $this.attr('data-math-style') === 'display';
+ try {
+ katex.render($this.text(), mathNode.get(0), { displayMode: display });
+ mathNode.insertAfter($this);
+ $this.remove();
+ } catch (err) {
+ // What can we do??
+ console.log(err.message);
+ }
+ });
+ };
+
+ $.fn.renderMath = function() {
+ var $this = this;
+ if ($this.length === 0) return;
+
+ if (katexLoaded) renderWithKaTeX($this);
+ else {
+ // Request CSS file so it is in the cache
+ $.get(gon.katex_css_url, function() {
+ var css = $('<link>',
+ { rel: 'stylesheet',
+ type: 'text/css',
+ href: gon.katex_css_url,
+ });
+ css.appendTo('head');
+
+ // Load KaTeX js
+ $.getScript(gon.katex_js_url, function() {
+ katexLoaded = true;
+ renderWithKaTeX($this); // Run KaTeX
+ });
+ });
+ }
+ };
+
+}).call(this);
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 440b5da756d..b1e844b7302 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, padded-blocks, max-len */
+/* global Cookies */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js
index 1d208f1494c..4b6ebadeac7 100644
--- a/app/assets/javascripts/search.js
+++ b/app/assets/javascripts/search.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, padded-blocks, max-len */
+/* global Api */
+
(function() {
this.Search = (function() {
function Search() {
diff --git a/app/assets/javascripts/search_autocomplete.js.es6 b/app/assets/javascripts/search_autocomplete.js.es6
index 5fa94556501..437f5dbbf7d 100644
--- a/app/assets/javascripts/search_autocomplete.js.es6
+++ b/app/assets/javascripts/search_autocomplete.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, no-plusplus, prefer-template, quotes, class-methods-use-this, no-unused-expressions, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, padded-blocks, no-extra-semi, indent, max-len */
+
((global) => {
const KEYCODE = {
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js
index fa2168723be..5ea00f408f4 100644
--- a/app/assets/javascripts/shortcuts.js
+++ b/app/assets/javascripts/shortcuts.js
@@ -1,4 +1,8 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, quotes, 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 */
+/* global Mousetrap */
+/* global Turbolinks */
+/* global findFileURL */
+
(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 65305b8c22f..c26903038b4 100644
--- a/app/assets/javascripts/shortcuts_blob.js
+++ b/app/assets/javascripts/shortcuts_blob.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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 */
+/* global Shortcuts */
+/* global Mousetrap */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/shortcuts_dashboard_navigation.js
index 1b9a265ba39..4549742bbcb 100644
--- a/app/assets/javascripts/shortcuts_dashboard_navigation.js
+++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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 */
+/* global Mousetrap */
+/* global Shortcuts */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js
index 68cd6fad04e..3a81380eef0 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/shortcuts_find_file.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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 */
+/* global Mousetrap */
+/* global ShortcutsNavigation */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index c4899f3566a..b892fbc3393 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -1,4 +1,8 @@
-/* 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 */
+/* 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, padded-blocks */
+/* global Mousetrap */
+/* global Turbolinks */
+/* global ShortcutsNavigation */
+/* global sidebar */
/*= require mousetrap */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
index 7d4d6364c70..0776d0a9b67 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/shortcuts_navigation.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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 */
+/* global Mousetrap */
+/* global Shortcuts */
/*= require shortcuts */
diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/shortcuts_network.js
index a4095d2c06b..ecc3fab84c3 100644
--- a/app/assets/javascripts/shortcuts_network.js
+++ b/app/assets/javascripts/shortcuts_network.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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, max-len */
+/* global Mousetrap */
+/* global ShortcutsNavigation */
/*= require shortcuts_navigation */
diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6
index a23ca449c4b..9790a44972d 100644
--- a/app/assets/javascripts/sidebar.js.es6
+++ b/app/assets/javascripts/sidebar.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable arrow-parens, class-methods-use-this, no-param-reassign, padded-blocks */
+/* global Cookies */
+
((global) => {
let singleton;
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 0d48e69cce9..ac8603ccd10 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -1,8 +1,9 @@
-/* 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 */
+/* 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, max-len */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
- this.SingleFileDiff = (function() {
+ window.SingleFileDiff = (function() {
var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER;
WRAPPER = '<div class="diff-content diff-wrap-lines"></div>';
@@ -93,7 +94,7 @@
$.fn.singleFileDiff = function(forceLoad, cb) {
return this.each(function() {
if (!$.data(this, 'singleFileDiff') || forceLoad) {
- return $.data(this, 'singleFileDiff', new SingleFileDiff(this, forceLoad, cb));
+ return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this, forceLoad, cb));
}
});
};
diff --git a/app/assets/javascripts/snippet/snippet_bundle.js b/app/assets/javascripts/snippet/snippet_bundle.js
index 2c8ecba7de4..18512d179b3 100644
--- a/app/assets/javascripts/snippet/snippet_bundle.js
+++ b/app/assets/javascripts/snippet/snippet_bundle.js
@@ -1,4 +1,6 @@
-/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, no-undef, quotes, semi, padded-blocks, max-len */
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, semi, padded-blocks, max-len */
+/* global ace */
+
/*= require_tree . */
(function() {
diff --git a/app/assets/javascripts/snippets_list.js.es6 b/app/assets/javascripts/snippets_list.js.es6
index c3afc3f2246..6f913326a3a 100644
--- a/app/assets/javascripts/snippets_list.js.es6
+++ b/app/assets/javascripts/snippets_list.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable arrow-parens, no-param-reassign, space-before-function-paren, func-names, no-var, semi, max-len */
+
(global => {
global.gl = global.gl || {};
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index 32803fa790b..f1fc526bf2e 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* 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, padded-blocks, max-len */
+/* global Flash */
+
(function() {
this.Star = (function() {
function Star() {
diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js
index bd37d69165f..5d0fa62c50a 100644
--- a/app/assets/javascripts/syntax_highlight.js
+++ b/app/assets/javascripts/syntax_highlight.js
@@ -1,4 +1,5 @@
-/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
+/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, 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
@@ -9,8 +10,10 @@
// <div class="js-syntax-highlight"></div>
//
(function() {
+
$.fn.syntaxHighlight = function() {
var $children;
+
if ($(this).hasClass('js-syntax-highlight')) {
// Given the element itself, apply highlighting
return $(this).addClass(gon.user_color_scheme);
@@ -23,8 +26,4 @@
}
};
- $(document).on('ready page:load', function() {
- return $('.js-syntax-highlight').syntaxHighlight();
- });
-
}).call(this);
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js.es6 b/app/assets/javascripts/templates/issuable_template_selector.js.es6
index 93a3d67ee9f..d2b152045b4 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js.es6
+++ b/app/assets/javascripts/templates/issuable_template_selector.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable prefer-const, comma-dangle, max-len, no-useless-return, object-curly-spacing, no-param-reassign, max-len */
+/* global Api */
+
/*= require ../blob/template_selector */
((global) => {
diff --git a/app/assets/javascripts/templates/issuable_template_selectors.js.es6 b/app/assets/javascripts/templates/issuable_template_selectors.js.es6
index 0a3890e85fe..7310b9de074 100644
--- a/app/assets/javascripts/templates/issuable_template_selectors.js.es6
+++ b/app/assets/javascripts/templates/issuable_template_selectors.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-new, comma-dangle, class-methods-use-this, prefer-const, no-param-reassign */
+
((global) => {
class IssuableTemplateSelectors {
constructor({ $dropdowns, editor } = {}) {
diff --git a/app/assets/javascripts/todos.js.es6 b/app/assets/javascripts/todos.js.es6
index 213e80825b7..d8713600030 100644
--- a/app/assets/javascripts/todos.js.es6
+++ b/app/assets/javascripts/todos.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable padded-blocks, class-methods-use-this, no-new, func-names, prefer-template, no-unneeded-ternary, object-shorthand, space-before-function-paren, comma-dangle, quote-props, consistent-return, no-else-return, semi, no-param-reassign, max-len */
+/* global UsersSelect */
+/* global Turbolinks */
+
((global) => {
class Todos {
@@ -72,7 +75,7 @@
allDoneClicked(e) {
e.preventDefault();
e.stopImmediatePropagation();
- $target = $(e.currentTarget);
+ const $target = $(e.currentTarget);
$target.disable();
return $.ajax({
type: 'POST',
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index 5d991542b51..d2aa3c7a841 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -1,4 +1,8 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, prefer-arrow-callback, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */
+/* global u2f */
+/* global U2FError */
+/* global U2FUtil */
+
// 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 4c70a6e9bb6..69f98c9c0ad 100644
--- a/app/assets/javascripts/u2f/error.js
+++ b/app/assets/javascripts/u2f/error.js
@@ -1,4 +1,6 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-console, quotes, prefer-template, padded-blocks, max-len */
+/* global u2f */
+
(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 97d8993cac2..4f5d68f546b 100644
--- a/app/assets/javascripts/u2f/register.js
+++ b/app/assets/javascripts/u2f/register.js
@@ -1,4 +1,8 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-else-return, quotes, quote-props, comma-dangle, one-var, one-var-declaration-per-line, padded-blocks, max-len */
+/* global u2f */
+/* global U2FError */
+/* global U2FUtil */
+
// 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/user.js.es6 b/app/assets/javascripts/user.js.es6
index 5e869e99fdb..0a2db7c05fe 100644
--- a/app/assets/javascripts/user.js.es6
+++ b/app/assets/javascripts/user.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign, semi */
+/* global Cookies */
+
((global) => {
global.User = class {
constructor({ action }) {
diff --git a/app/assets/javascripts/user_tabs.js.es6 b/app/assets/javascripts/user_tabs.js.es6
index 5a625611987..b9c23b51b4d 100644
--- a/app/assets/javascripts/user_tabs.js.es6
+++ b/app/assets/javascripts/user_tabs.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, array-bracket-spacing, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, semi, no-param-reassign */
+
/*
UserTabs
diff --git a/app/assets/javascripts/username_validator.js.es6 b/app/assets/javascripts/username_validator.js.es6
index c4dde575c6e..137cefa3b8e 100644
--- a/app/assets/javascripts/username_validator.js.es6
+++ b/app/assets/javascripts/username_validator.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, consistent-return, class-methods-use-this, arrow-parens, no-param-reassign, max-len */
+
((global) => {
const debounceTimeoutDuration = 1000;
const invalidInputClass = 'gl-field-error-outline';
@@ -77,7 +78,7 @@
this.renderState();
return $.ajax({
type: 'GET',
- url: `/users/${username}/exists`,
+ url: `${gon.relative_url_root}/users/${username}/exists`,
dataType: 'json',
success: (res) => this.setAvailabilityState(res.exists)
});
diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js
index ba7f533c349..578be7c3590 100644
--- a/app/assets/javascripts/users/calendar.js
+++ b/app/assets/javascripts/users/calendar.js
@@ -1,4 +1,7 @@
-/* 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 */
+/* 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, 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 */
+/* global d3 */
+/* global dateFormat */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index c6e18fad832..d4b5e03aa35 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,4 +1,8 @@
-/* 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 */
+/* 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, 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 */
+/* global Vue */
+/* global Issuable */
+/* global ListUser */
+
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
slice = [].slice;
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index 82eb761442a..e09b59dd5aa 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -1,4 +1,7 @@
-/* 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 */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, padded-blocks, max-len */
+/* global Dropzone */
+/* global Mousetrap */
+
// Zen Mode (full screen) textarea
//
/*= provides zen_mode:enter */
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index c82a9a2b9e3..40bc0579393 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -44,3 +44,6 @@
@import "framework/awards.scss";
@import "framework/images.scss";
@import "framework/broadcast-messages";
+@import "framework/emojis.scss";
+@import "framework/icons.scss";
+@import "framework/snippets.scss";
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index dece5c3202b..9fc9bcebc44 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -12,8 +12,8 @@
z-index: 9;
width: 300px;
font-size: 14px;
- background-color: $award-emoji-menu-bg;
- border: 1px solid $award-emoji-menu-border;
+ background-color: $white-light;
+ border: 1px solid $border-white-light;
border-radius: $border-radius-base;
box-shadow: 0 6px 12px $award-emoji-menu-shadow;
pointer-events: none;
@@ -135,7 +135,7 @@
}
.award-control-icon {
- color: $award-emoji-new-btn-icon-color;
+ color: $border-gray-normal;
margin-top: 1px;
}
}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 95c02499271..9f02749f5ab 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -24,7 +24,7 @@
.row-content-block {
margin-top: 0;
margin-bottom: -$gl-padding;
- background-color: $background-color;
+ background-color: $gray-light;
padding: $gl-padding;
margin-bottom: 0;
border-top: 1px solid $white-dark;
@@ -118,7 +118,7 @@
.cover-block {
text-align: center;
- background: $background-color;
+ background: $gray-light;
padding-top: 44px;
position: relative;
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 1c7b2f4df7c..59ff17ad2c1 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -88,11 +88,11 @@
}
@mixin btn-gray {
- @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, $gl-gray-dark);
+ @include btn-color($gray-light, $border-gray-normal, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, $gl-gray-dark);
}
@mixin btn-white {
- @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $gl-text-color);
+ @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-gray-dark, $gl-text-color);
}
@mixin btn-with-margin {
@@ -235,7 +235,7 @@
}
.btn-transparent {
- color: $btn-transparent-color;
+ color: $gl-gray-light;
background-color: transparent;
border: 0;
@@ -289,7 +289,7 @@
.active {
box-shadow: $gl-btn-active-background;
- border: 1px solid $border-white-dark !important;
+ border: 1px solid $border-gray-dark !important;
background-color: $btn-active-gray-light !important;
}
}
@@ -309,8 +309,8 @@
text-align: left;
padding: 6px 16px;
border-color: $border-color;
- color: $btn-placeholder-gray;
- background-color: $background-color;
+ color: $gray-darkest;
+ background-color: $gray-light;
&:hover,
&:active,
@@ -318,8 +318,8 @@
cursor: text;
box-shadow: none;
border-color: $border-color;
- color: $btn-placeholder-gray;
- background-color: $background-color;
+ color: $gray-darkest;
+ background-color: $gray-light;
}
}
@@ -331,7 +331,7 @@
margin-left: 10px;
i {
- color: $gl-icon-color;
+ color: $gl-gray-light;
}
}
@@ -344,8 +344,8 @@
}
.btn-static {
- background-color: $background-color !important;
- border: 1px solid $border-gray-light;
+ background-color: $gray-light !important;
+ border: 1px solid $border-gray-normal;
cursor: default;
&:active {
diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss
index 2a100980aca..e0e46dd73af 100644
--- a/app/assets/stylesheets/framework/callout.scss
+++ b/app/assets/stylesheets/framework/callout.scss
@@ -11,7 +11,7 @@
padding: $gl-padding;
border-left: 3px solid $border-color;
color: $text-color;
- background: $background-color;
+ background: $gray-light;
}
.bs-callout h4 {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index d5914b900e2..11adf2568a1 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -10,7 +10,7 @@
@mixin chevron-active {
.fa-chevron-down {
- color: $dropdown-toggle-hover-icon-color;
+ color: $gray-darkest;
}
}
@@ -28,14 +28,14 @@
.dropdown-toggle,
.dropdown-menu-toggle {
@include chevron-active;
- border-color: $dropdown-toggle-hover-border-color;
+ border-color: $gray-darkest;
}
}
.dropdown-toggle {
padding: 6px 8px 6px 10px;
- background-color: $dropdown-toggle-bg;
- color: $dropdown-toggle-color;
+ background-color: $white-light;
+ color: $gl-text-color;
font-size: 14px;
text-align: left;
border: 1px solid $border-color;
@@ -73,7 +73,7 @@
}
.fa {
- color: $dropdown-toggle-icon-color;
+ color: $gray-darkest;
}
.fa-chevron-down {
@@ -85,7 +85,7 @@
&:hover {
@include chevron-active;
- border-color: $dropdown-toggle-hover-border-color;
+ border-color: $gray-darkest;
}
&:focus:active {
@@ -131,7 +131,7 @@
font-size: 14px;
font-weight: normal;
padding: 8px 0;
- background-color: $dropdown-bg;
+ background-color: $white-light;
border: 1px solid $dropdown-border-color;
border-radius: $border-radius-base;
box-shadow: 0 2px 4px $dropdown-shadow-color;
@@ -188,7 +188,6 @@
&.is-focused {
background-color: $dropdown-link-hover-bg;
text-decoration: none;
- outline: 0;
}
&.dropdown-menu-empty-link {
@@ -202,7 +201,7 @@
}
.icon-play {
- fill: $table-text-gray;
+ fill: $gl-gray-light;
margin-right: 6px;
height: 12px;
width: 11px;
@@ -210,7 +209,7 @@
}
.dropdown-header {
- color: $dropdown-header-color;
+ color: $gl-gray-light;
font-size: 13px;
line-height: 22px;
padding: 0 10px;
@@ -223,7 +222,7 @@
.unclickable {
cursor: not-allowed;
padding: 5px 8px;
- color: $dropdown-header-color;
+ color: $gl-gray-light;
}
}
@@ -602,14 +601,14 @@
th {
padding: 2px 0;
- color: $calendar-header-color;
+ color: $note-disabled-comment-color;
font-weight: normal;
text-transform: lowercase;
border-top: 1px solid $calendar-border-color;
}
.ui-datepicker-unselectable {
- background-color: $calendar-unselectable-bg;
+ background-color: $gray-light;
}
}
@@ -621,11 +620,11 @@
.dropdown-menu-inner-content {
display: block;
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
}
.dropdown-toggle-text {
&.is-default {
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
}
}
diff --git a/app/assets/stylesheets/pages/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index f17797b2381..7158de65143 100644
--- a/app/assets/stylesheets/pages/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -1,4 +1,4 @@
-.emoji-0023-20E3 { background-position: 0 0px; }
+.emoji-0023-20E3 { background-position: 0 0; }
.emoji-002A-20E3 { background-position: -20px 0; }
.emoji-0030-20E3 { background-position: 0 -20px; }
.emoji-0031-20E3 { background-position: -20px -20px; }
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index ab0b81f77f7..88ed0a4a17e 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -19,7 +19,7 @@
.file-title {
position: relative;
- background-color: $background-color;
+ background-color: $gray-light;
border-bottom: 1px solid $border-color;
margin: 0;
text-align: left;
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 25a2b38baaa..940807fc399 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -22,7 +22,7 @@ input[type='text'].danger {
margin-top: 0;
margin-bottom: -$gl-padding;
padding: $gl-padding;
- background-color: $background-color;
+ background-color: $gray-light;
border-top: 1px solid $border-color;
}
@@ -149,7 +149,7 @@ label {
}
.form-control::-webkit-input-placeholder {
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
}
.input-group {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index cc2286038c0..971940773f7 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -9,7 +9,7 @@ header {
&.navbar-empty {
height: $header-height;
background: $white-light;
- border-bottom: 1px solid $btn-gray-hover;
+ border-bottom: 1px solid $white-normal;
.center-logo {
margin: 8px 0;
@@ -27,7 +27,7 @@ header {
z-index: 100;
margin-bottom: 0;
height: $header-height;
- background-color: $background-color;
+ background-color: $gray-light;
border: none;
border-bottom: 1px solid $border-color;
@@ -45,7 +45,7 @@ header {
padding: 0;
.nav > li > a {
- color: $gl-icon-color;
+ color: $gl-gray-light;
font-size: 18px;
padding: 0;
margin: ($header-height - 28) / 2 0;
@@ -62,8 +62,8 @@ header {
&:hover,
&:focus,
&:active {
- background-color: $background-color;
- color: darken($gl-icon-color, 30%);
+ background-color: $gray-light;
+ color: darken($gl-gray-light, 30%);
.todos-pending-count {
background: darken($todo-alert-blue, 10%);
@@ -84,11 +84,11 @@ header {
padding: 6px 10px;
&:hover {
- background-color: $btn-gray-hover;
+ background-color: $white-normal;
}
&.active {
- color: $gl-icon-color;
+ color: $gl-gray-light;
}
}
}
@@ -100,10 +100,10 @@ header {
font-size: 18px;
padding: 6px 10px;
border: none;
- background-color: $background-color;
+ background-color: $gray-light;
&:hover {
- background-color: $btn-gray-hover;
+ background-color: $white-normal;
}
}
}
diff --git a/app/assets/stylesheets/pages/icons.scss b/app/assets/stylesheets/framework/icons.scss
index 226bd2ead31..226bd2ead31 100644
--- a/app/assets/stylesheets/pages/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss
index 878f44116ba..09a569ad415 100644
--- a/app/assets/stylesheets/framework/images.scss
+++ b/app/assets/stylesheets/framework/images.scss
@@ -4,7 +4,7 @@
}
.appearance-light-logo-preview {
- background-color: $background-color;
+ background-color: $gray-light;
max-width: 72px;
padding: 10px;
margin-bottom: 10px;
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 44834a84234..298913108ee 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -41,6 +41,6 @@
}
&.status-box-upcoming {
- background: $issue-box-upcoming-bg;
+ background: $gl-gray-light;
}
}
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index ed4b60faf92..e96cd671e34 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -36,7 +36,7 @@
color: $list-warning-row-color;
}
- &.smoke { background-color: $background-color; }
+ &.smoke { background-color: $gray-light; }
&:not(.ui-sort-disabled):hover {
background: $row-hover;
@@ -46,7 +46,7 @@
border-bottom: none;
&.bottom {
- background: $background-color;
+ background: $gray-light;
}
}
@@ -59,7 +59,7 @@
p {
padding-top: 1px;
margin: 0;
- color: $gray-dark;
+ color: $white-normal;
img {
position: relative;
@@ -113,7 +113,7 @@ ul.content-list {
padding: 0;
li {
- border-color: $table-border-color;
+ border-color: $white-normal;
font-size: $list-font-size;
color: $list-text-color;
@@ -186,7 +186,7 @@ ul.content-list {
&.list-placeholder {
background-color: $gray-light;
- border: dotted 1px $gray-dark;
+ border: dotted 1px $white-normal;
margin: 1px 0;
min-height: 52px;
}
@@ -224,7 +224,7 @@ ul.content-list {
}
.label-default {
- color: $btn-transparent-color;
+ color: $gl-gray-light;
}
}
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 59a30d31ac7..e30d81d09f0 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -114,7 +114,7 @@
// Border around images in issue and MR comments.
img:not(.emoji) {
- border: 1px solid $table-border-gray;
+ border: 1px solid $white-normal;
padding: 5px;
margin: 5px 0;
// Ensure that image does not exceed viewport
@@ -135,7 +135,7 @@
.toolbar-btn {
float: left;
padding: 0 5px;
- color: $note-toolbar-color;
+ color: $gl-gray-light;
background: transparent;
border: 0;
outline: 0;
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index ea77348633d..e4affbb1be1 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -51,7 +51,7 @@
margin-bottom: -1px;
font-size: 14px;
line-height: 28px;
- color: $note-toolbar-color;
+ color: $gl-gray-light;
border-bottom: 2px solid transparent;
&:hover,
@@ -80,17 +80,17 @@
.badge {
font-weight: normal;
background-color: $nav-badge-bg;
- color: $btn-transparent-color;
+ color: $gl-gray-light;
vertical-align: baseline;
}
}
&.sub-nav {
text-align: center;
- background-color: $dark-background-color;
+ background-color: $gray-normal;
.container-fluid {
- background-color: $dark-background-color;
+ background-color: $gray-normal;
margin-bottom: 0;
}
@@ -117,7 +117,7 @@
.top-area {
@include clearfix;
- border-bottom: 1px solid $btn-gray-hover;
+ border-bottom: 1px solid $white-normal;
.nav-text {
padding-top: 16px;
@@ -289,7 +289,7 @@
top: $header-height;
width: 100%;
z-index: 11;
- background: $background-color;
+ background: $gray-light;
border-bottom: 1px solid $border-color;
transition: padding $sidebar-transition-duration;
text-align: center;
@@ -317,7 +317,7 @@
.fa-caret-down {
margin-left: 5px;
- color: $gl-icon-color;
+ color: $gl-gray-light;
}
.dropdown {
@@ -352,7 +352,7 @@
}
.fade-right {
- @include fade(left, $background-color);
+ @include fade(left, $gray-light);
right: -5px;
.fa {
@@ -361,7 +361,7 @@
}
.fade-left {
- @include fade(right, $background-color);
+ @include fade(right, $gray-light);
left: -5px;
.fa {
@@ -372,7 +372,7 @@
&.sub-nav-scroll {
.fade-right {
- @include fade(left, $dark-background-color);
+ @include fade(left, $gray-normal);
right: 0;
.fa {
@@ -381,7 +381,7 @@
}
.fade-left {
- @include fade(right, $dark-background-color);
+ @include fade(right, $gray-normal);
left: 0;
.fa {
diff --git a/app/assets/stylesheets/framework/page-header.scss b/app/assets/stylesheets/framework/page-header.scss
index 85c1385d5d9..fff7d7f7524 100644
--- a/app/assets/stylesheets/framework/page-header.scss
+++ b/app/assets/stylesheets/framework/page-header.scss
@@ -14,7 +14,7 @@
.header-action-buttons {
i {
- color: $gl-icon-color;
+ color: $gl-gray-light;
font-size: 13px;
margin-right: 3px;
}
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index fde1431b13e..9ab17e67d4c 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -39,7 +39,7 @@
}
&:hover {
- background-color: $gray-dark;
+ background-color: $white-normal;
border-color: $border-white-normal;
color: $gl-text-color;
}
@@ -108,7 +108,7 @@
border-color: $input-border;
color: $gl-text-color;
line-height: 15px;
- background-color: $background-color;
+ background-color: $gray-light;
background-image: none;
.select2-search-choice-close {
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 0aa609b8dd5..46a06cd7eab 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -23,7 +23,7 @@
.sidebar-wrapper {
z-index: 1000;
- background: $background-color;
+ background: $gray-light;
.nicescroll-rails-hr {
// TODO: Figure out why nicescroll doesn't hide horizontal bar
diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/framework/snippets.scss
index ff13b86acf0..5f7e1b17cc7 100644
--- a/app/assets/stylesheets/pages/snippets.scss
+++ b/app/assets/stylesheets/framework/snippets.scss
@@ -22,16 +22,6 @@
.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-header {
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 5d0ca63ea08..6d9fa74a030 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -31,7 +31,7 @@ table {
}
th {
- background-color: $background-color;
+ background-color: $gray-light;
font-weight: normal;
border-bottom: none;
@@ -41,7 +41,7 @@ table {
}
td {
- border-color: $table-border-color;
+ border-color: $white-normal;
}
}
}
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 875cded8b4e..6078505807e 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -6,7 +6,7 @@
.timeline-entry {
padding: $gl-padding $gl-btn-padding 11px;
- border-color: $table-border-color;
+ border-color: $white-normal;
color: $gl-gray;
border-bottom: 1px solid $border-white-light;
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index 55bc325b858..d998d654aa4 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -33,9 +33,9 @@
@import "bootstrap/labels";
@import "bootstrap/badges";
@import "bootstrap/alerts";
-@import "bootstrap/progress-bars";
+// @import "bootstrap/progress-bars";
@import "bootstrap/list-group";
-@import "bootstrap/wells";
+// @import "bootstrap/wells";
@import "bootstrap/close";
@import "bootstrap/panels";
diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index c731a8f222f..876adf7f712 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -78,7 +78,7 @@ $pagination-active-bg: $white-light;
$pagination-active-border: $border-color;
$pagination-disabled-color: #cdcdcd;
-$pagination-disabled-bg: $background-color;
+$pagination-disabled-bg: $gray-light;
$pagination-disabled-border: $border-color;
@@ -117,8 +117,8 @@ $alert-border-radius: 0;
$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-default-heading-bg: $gray-light;
+$panel-footer-bg: $gray-light;
$panel-inner-border: $border-color;
//== Wells
@@ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding;
//== Code
//
//##
-$pre-bg: $background-color !default;
+$pre-bg: $gray-light !default;
$pre-color: $gl-gray !default;
$pre-border-color: $border-color;
-$table-bg-accent: $background-color;
+$table-bg-accent: $gray-light;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index a1d5f6427f4..936aaf38254 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -17,16 +17,15 @@ $darken-dark-factor: 10%;
$darken-border-factor: 5%;
$white-light: #fff;
-$white-normal: darken($white-light, $darken-normal-factor);
-$white-dark: darken($white-light, $darken-dark-factor);
+$white-normal: #f0f0f0;
+$white-dark: #eaeaea;
$gray-lightest: #fdfdfd;
$gray-light: #fafafa;
$gray-lighter: #f9f9f9;
-$gray-normal: darken($gray-light, $darken-normal-factor);
-$gray-dark: darken($gray-light, $darken-dark-factor);
+$gray-normal: #f5f5f5;
$gray-darker: #eee;
-$gray-darkest: #c9c9c9;
+$gray-darkest: #c4c4c4;
$green-light: #3cbd70;
$green-normal: darken($green-light, $darken-normal-factor);
@@ -55,11 +54,9 @@ $black-transparent: rgba(0, 0, 0, 0.3);
$border-white-light: darken($white-light, $darken-border-factor);
$border-white-normal: darken($white-normal, $darken-border-factor);
-$border-white-dark: darken($white-dark, $darken-border-factor);
-$border-gray-light: darken($gray-light, $darken-border-factor);
$border-gray-normal: darken($gray-normal, $darken-border-factor);
-$border-gray-dark: darken($gray-dark, $darken-border-factor);
+$border-gray-dark: darken($white-normal, $darken-border-factor);
$border-green-extra-light: #9adb84;
$border-green-light: darken($green-light, $darken-border-factor);
@@ -78,9 +75,6 @@ $border-red-light: darken($red-light, $darken-border-factor);
$border-red-normal: darken($red-normal, $darken-border-factor);
$border-red-dark: darken($red-dark, $darken-border-factor);
-$help-well-bg: $gray-light;
-$help-well-border: #e5e5e5;
-
$warning-message-bg: #fbf2d9;
$warning-message-color: #9e8e60;
$warning-message-border: #f0e2bb;
@@ -90,10 +84,6 @@ $warning-message-border: #f0e2bb;
*/
$border-color: #e5e5e5;
$focus-border-color: #3aabf0;
-$table-border-color: #f0f0f0;
-$background-color: $gray-light;
-$dark-background-color: #f5f5f5;
-$table-text-gray: #8f8f8f;
$well-expand-item: #e8f2f7;
$well-inner-border: #eef0f2;
$well-light-border: #f1f1f1;
@@ -113,12 +103,10 @@ $gl-text-orange: #d90;
$gl-link-color: #3777b0;
$gl-diff-text-color: #555;
$gl-dark-link-color: #333;
-$gl-placeholder-color: #8f8f8f;
-$gl-icon-color: $gl-placeholder-color;
+$gl-gray-light: #8f8f8f;
$gl-grayish-blue: #7f8fa4;
$gl-gray: $gl-text-color;
$gl-gray-dark: #313236;
-$gl-gray-light: $gl-placeholder-color;
$gl-header-color: #4c4e54;
/*
@@ -168,9 +156,7 @@ $header-height: 50px;
$fixed-layout-width: 1280px;
$error-exclamation-point: #e62958;
$border-radius-default: 2px;
-$btn-transparent-color: #8f8f8f;
$settings-icon-size: 18px;
-$provider-btn-group-border: #e5e5e5;
$provider-btn-not-active-color: #4688f1;
$link-underline-blue: #4a8bee;
$active-item-blue: #4a8bee;
@@ -193,7 +179,6 @@ $count-arrow-border: #dce0e5;
$save-project-loader-color: #555;
$divergence-graph-bar-bg: #ccc;
$divergence-graph-separator-bg: #ccc;
-$issue-box-upcoming-bg: #8f8f8f;
/*
* Common component specific colors
@@ -246,8 +231,6 @@ $line-removed-dark: #fac5cd;
$line-number-old: #f9d7dc;
$line-number-new: #ddfbe6;
$line-number-select: #fbf2da;
-$match-line: $gray-light;
-$table-border-gray: #f0f0f0;
$line-target-blue: #f6faff;
$line-select-yellow: #fcf8e7;
$line-select-yellow-dark: #f0e2bd;
@@ -257,7 +240,6 @@ $file-mode-changed: #777;
$file-mode-changed: #777;
$diff-image-bg: #ddd;
$diff-image-info-color: grey;
-$diff-image-img-bg: #e5e5e5;
$diff-swipe-border: #999;
$diff-view-modes-color: grey;
$diff-view-modes-border: #c1c1c1;
@@ -272,14 +254,12 @@ $regular_font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-San
* Dropdowns
*/
$dropdown-width: 300px;
-$dropdown-bg: #fff;
$dropdown-link-color: #555;
$dropdown-link-hover-bg: $row-hover;
$dropdown-empty-row-bg: rgba(#000, .04);
$dropdown-border-color: $border-color;
$dropdown-shadow-color: rgba(#000, .1);
$dropdown-divider-color: rgba(#000, .1);
-$dropdown-header-color: #959494;
$dropdown-title-btn-color: #bfbfbf;
$dropdown-input-color: #555;
$dropdown-input-fa-color: #c7c7c7;
@@ -287,31 +267,19 @@ $dropdown-input-focus-border: $focus-border-color;
$dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4);
$dropdown-loading-bg: rgba(#fff, .6);
$dropdown-chevron-size: 10px;
+$dropdown-toggle-active-border-color: darken($border-color, 14%);
-$dropdown-toggle-bg: #fff;
-$dropdown-toggle-color: #5c5c5c;
-$dropdown-toggle-border-color: #e5e5e5;
-$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 13%);
-$dropdown-toggle-active-border-color: darken($dropdown-toggle-border-color, 14%);
-$dropdown-toggle-icon-color: #c4c4c4;
-$dropdown-toggle-hover-icon-color: darken($dropdown-toggle-icon-color, 7%);
/*
* Buttons
*/
$btn-active-gray: #ececec;
$btn-active-gray-light: e4e7ed;
-$btn-placeholder-gray: #c7c7c7;
-$btn-white-active: #848484;
-$btn-gray-hover: #eee;
/*
* Award emoji
*/
-$award-emoji-menu-bg: #fff;
-$award-emoji-menu-border: #f1f2f4;
$award-emoji-menu-shadow: rgba(0,0,0,.175);
-$award-emoji-new-btn-icon-color: #dcdcdc;
/*
* Search Box
@@ -319,22 +287,15 @@ $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: 220px;
-$location-badge-color: #aaa;
-$location-badge-bg: $dark-background-color;
$location-badge-active-bg: #4f91f8;
$location-icon-color: #e7e9ed;
-$location-icon-active-color: #807e7e;
/*
* Notes
*/
$notes-light-color: #8e8e8e;
-$notes-action-color: #c3c3c3;
$notes-role-color: #8e8e8e;
-$notes-role-border-color: #e4e4e4;
$note-disabled-comment-color: #b2b2b2;
-$note-form-border-color: #e5e5e5;
-$note-toolbar-color: #959494;
$note-targe3-outside: #fffff0;
$note-targe3-inside: #ffffd3;
$note-line2-border: #ddd;
@@ -344,15 +305,12 @@ $note-line2-border: #ddd;
* Zen
*/
$zen-control-color: #555;
-$zen-control-hover-color: #111;
/*
* Calendar
*/
-$calendar-header-color: #b8b8b8;
$calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1);
-$calendar-unselectable-bg: $gray-light;
$calendar-user-contrib-text: #959494;
/*
@@ -366,15 +324,8 @@ $cycle-analytics-light-gray: #bfbfbf;
$cycle-analytics-dismiss-icon-color: #b2b2b2;
/*
- * Personal Access Tokens
- */
-$personal-access-tokens-disabled-label-color: #bbb;
-
-/*
* CI
*/
-$ci-output-bg: #1d1f21;
-$ci-text-color: #c5c8c6;
$ci-skipped-color: #888;
/*
@@ -470,7 +421,6 @@ $help-shortcut-header-color: #333;
/*
* Issues
*/
-$issues-border: #e5e5e5;
$issues-today-bg: #f3fff2;
$issues-today-border: #e1e8d5;
diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss
index f2860dfe84d..f9c850ebdc8 100644
--- a/app/assets/stylesheets/framework/wells.scss
+++ b/app/assets/stylesheets/framework/wells.scss
@@ -1,5 +1,5 @@
.info-well {
- background: $background-color;
+ background: $gray-light;
color: $gl-gray;
border: 1px solid $border-color;
border-radius: $border-radius-default;
@@ -45,7 +45,7 @@
}
.light-well {
- background-color: $background-color;
+ background-color: $gray-light;
padding: 15px;
}
diff --git a/app/assets/stylesheets/framework/zen.scss b/app/assets/stylesheets/framework/zen.scss
index e5c7d70d45a..84b639fabf5 100644
--- a/app/assets/stylesheets/framework/zen.scss
+++ b/app/assets/stylesheets/framework/zen.scss
@@ -40,7 +40,7 @@
}
.zen-control-full {
- color: $note-toolbar-color;
+ color: $gl-gray-light;
&:hover {
color: $gl-link-color;
@@ -57,6 +57,6 @@
font-size: 36px;
&:hover {
- color: $zen-control-hover-color;
+ color: $black;
}
}
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 1adab3ffd94..54a5664a874 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -69,14 +69,14 @@ $white-gc-bg: #eaf2f5;
@mixin matchLine {
color: $black-transparent;
- background-color: $match-line;
+ background-color: $gray-light;
}
.code.white {
// Line numbers
.line-numbers,
.diff-line-num {
- background-color: $background-color;
+ background-color: $gray-light;
}
.diff-line-num,
@@ -87,7 +87,7 @@ $white-gc-bg: #eaf2f5;
// Code itself
pre.code,
.diff-line-num {
- border-color: $table-border-gray;
+ border-color: $white-normal;
}
&,
diff --git a/app/assets/stylesheets/mailers/highlighted_diff_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
index 024b4df6bd0..60ff72c703e 100644
--- a/app/assets/stylesheets/mailers/highlighted_diff_email.scss
+++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
@@ -91,9 +91,9 @@ $highlighted-gc-bg: #eaf2f5;
padding: 0 5px;
text-align: right;
width: 35px;
- background-color: $background-color;
+ background-color: $gray-light;
color: $black-transparent;
- border-right: 1px solid $table-border-gray;
+ border-right: 1px solid $white-normal;
&.old {
background-color: $line-number-old;
@@ -130,7 +130,7 @@ $highlighted-gc-bg: #eaf2f5;
&.match {
color: $black-transparent;
- background-color: $match-line;
+ background-color: $gray-light;
}
}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 0d9cf679e7c..c735f104c20 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -98,7 +98,7 @@
.board-inner {
height: 100%;
font-size: $issue-boards-font-size;
- background: $background-color;
+ background: $gray-light;
border: 1px solid $border-color;
border-radius: $border-radius-default;
}
@@ -253,7 +253,7 @@
.board-list-count {
padding: 10px 0;
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
font-size: 13px;
> .fa {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index dcc13f6d74a..66f7e7f97c8 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -42,7 +42,7 @@
}
.environment-information {
- background-color: $background-color;
+ background-color: $gray-light;
border: 1px solid $border-color;
padding: 12px $gl-padding;
border-radius: $border-radius-default;
@@ -96,8 +96,8 @@
}
.build-trace {
- background: $ci-output-bg;
- color: $ci-text-color;
+ background: $black;
+ color: $gray-darkest;
white-space: pre;
overflow-x: auto;
font-size: 12px;
@@ -257,7 +257,7 @@
}
.build-light-text {
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
}
.build-gutter-toggle {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index c29b5fdea78..e76e1a73b25 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -7,7 +7,7 @@
.commit-header {
padding: 5px 10px;
- background-color: $background-color;
+ background-color: $gray-light;
border-top: 1px solid $gray-darker;
border-bottom: 1px solid $gray-darker;
font-size: 14px;
@@ -38,7 +38,7 @@
.text-expander {
display: inline-block;
background: $gray-light;
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
padding: 0 5px;
cursor: pointer;
border: 1px solid $border-gray-dark;
@@ -117,7 +117,7 @@
.commit-row-description {
font-size: 14px;
- border-left: 1px solid $btn-gray-hover;
+ border-left: 1px solid $white-normal;
padding: 10px 15px;
margin: 10px 0;
background: $gray-light;
@@ -174,7 +174,7 @@
height: 14px;
width: 14px;
vertical-align: middle;
- fill: $table-text-gray;
+ fill: $gl-gray-light;
}
}
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 737f6e0f4be..f30795fd2c2 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -11,7 +11,7 @@
.diff-header {
position: relative;
- background: $background-color;
+ background: $gray-light;
border-bottom: 1px solid $border-color;
padding: 10px 16px;
color: $gl-diff-text-color;
@@ -38,7 +38,7 @@
cursor: pointer;
&:hover {
- background-color: $dark-background-color;
+ background-color: $gray-normal;
}
.diff-toggle-caret {
@@ -187,8 +187,8 @@
img {
border: 1px solid $white-light;
- background-image: linear-gradient(45deg, $diff-image-img-bg 25%, transparent 25%, transparent 75%, $diff-image-img-bg 75%, $diff-image-img-bg 100%),
- linear-gradient(45deg, $diff-image-img-bg 25%, transparent 25%, transparent 75%, $diff-image-img-bg 75%, $diff-image-img-bg 100%);
+ background-image: linear-gradient(45deg, $border-color 25%, transparent 25%, transparent 75%, $border-color 75%, $border-color 100%),
+ linear-gradient(45deg, $border-color 25%, transparent 25%, transparent 75%, $border-color 75%, $border-color 100%);
background-size: 10px 10px;
background-position: 0 0, 5px 5px;
max-width: 100%;
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 6cde9c592de..3fdb4f510fa 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -10,7 +10,7 @@
}
.ace_gutter-cell {
- background-color: $background-color;
+ background-color: $gray-light;
}
.cancel-btn {
@@ -34,7 +34,7 @@
}
.editor-ref {
- background: $background-color;
+ background: $gray-light;
padding-right: $gl-padding;
border-right: 1px solid $border-color;
display: block;
@@ -51,8 +51,16 @@
.new-file-name {
display: inline-block;
- width: 450px;
+ max-width: 450px;
float: left;
+
+ @media(max-width: $screen-md-max) {
+ width: 280px;
+ }
+
+ @media(max-width: $screen-sm-max) {
+ width: 180px;
+ }
}
.file-buttons {
@@ -114,3 +122,42 @@
}
}
}
+
+@media(max-width: $screen-xs-max){
+ .file-editor {
+ .file-title {
+ .pull-right {
+ height: auto;
+ }
+ }
+
+ .new-file-name {
+ max-width: none;
+ width: 100%;
+ margin-bottom: 3px;
+ }
+
+ .file-buttons {
+ display: block;
+ width: 100%;
+ margin-bottom: 10px;
+
+ .soft-wrap-toggle {
+ width: 100%;
+ margin: 3px 0;
+ }
+
+ .encoding-selector,
+ .license-selector,
+ .gitignore-selector,
+ .gitlab-ci-yml-selector {
+ display: block;
+ margin: 3px 0;
+
+ button {
+ width: 100%;
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index e716f24c8e2..92dd9885ab8 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -66,14 +66,14 @@
.external-url,
.dropdown-new {
- color: $table-text-gray;
+ color: $gl-gray-light;
}
.dropdown-menu {
.fa {
margin-right: 6px;
- color: $table-text-gray;
+ color: $gl-gray-light;
}
}
@@ -84,7 +84,7 @@
.stop-env-link,
.external-url {
- color: $table-text-gray;
+ color: $gl-gray-light;
.stop-env-icon {
font-size: 14px;
@@ -119,7 +119,7 @@
.badge {
font-weight: normal;
background-color: $gray-darker;
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
vertical-align: baseline;
}
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index dc67d411c71..98925c2d0cb 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -5,7 +5,7 @@
.event-item {
font-size: $gl-font-size;
padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top);
- border-bottom: 1px solid $table-border-color;
+ border-bottom: 1px solid $white-normal;
color: $list-text-color;
&.event-inline {
diff --git a/app/assets/stylesheets/pages/explore.scss b/app/assets/stylesheets/pages/explore.scss
deleted file mode 100644
index 9b92128624c..00000000000
--- a/app/assets/stylesheets/pages/explore.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.explore-title {
- text-align: center;
-
- h3 {
- font-weight: normal;
- font-size: 30px;
- }
-}
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index e2e644dc23b..dae8ccdef6c 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -60,7 +60,7 @@
// Border around images in the help pages.
img:not(.emoji) {
- border: 1px solid $table-border-gray;
+ border: 1px solid $white-normal;
padding: 5px;
margin: 5px;
max-height: calc(100vh - 100px);
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 407c0afbac8..0234f2d49e7 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -7,7 +7,7 @@
// Border around images in issue and MR descriptions.
.description img:not(.emoji) {
- border: 1px solid $table-border-gray;
+ border: 1px solid $white-normal;
padding: 5px;
margin: 5px;
max-height: calc(100vh - 100px);
@@ -51,7 +51,7 @@
.block {
@include clearfix;
padding: $gl-padding 0;
- border-bottom: 1px solid $border-gray-light;
+ border-bottom: 1px solid $border-gray-normal;
// This prevents the mess when resizing the sidebar
// of elements repositioning themselves..
width: $gutter_inner_width;
@@ -169,7 +169,7 @@
}
.no-value {
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
}
.sidebar-collapsed-icon {
@@ -178,7 +178,7 @@
.gutter-toggle {
margin-top: 7px;
- border-left: 1px solid $border-gray-light;
+ border-left: 1px solid $border-gray-normal;
}
.assignee .avatar {
@@ -216,7 +216,7 @@
}
.participants {
- border-bottom: 1px solid $border-gray-light;
+ border-bottom: 1px solid $border-gray-normal;
}
.hide-collapsed {
@@ -333,7 +333,7 @@
margin-left: 5px;
a {
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
}
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 3b47f99df2c..8734a3b1598 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -88,12 +88,12 @@ ul.related-merge-requests > li {
&.closed {
background: $gray-light;
- border-color: $issues-border;
+ border-color: $border-color;
}
&.merged {
background: $gray-light;
- border-color: $issues-border;
+ border-color: $border-color;
}
}
@@ -144,7 +144,7 @@ ul.related-merge-requests > li {
}
.btn {
- background-color: $background-color;
- border: 1px solid $border-gray-light;
+ background-color: $gray-light;
+ border: 1px solid $border-gray-normal;
}
}
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index 7a90713dd3f..5a9f199fb34 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -274,7 +274,7 @@ $colors: (
}
.discard-changes-alert {
- background-color: $background-color;
+ background-color: $gray-light;
text-align: right;
padding: $gl-padding-top $gl-padding;
color: $gl-text-color;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 6234779ac19..e779e65eca3 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -3,7 +3,7 @@
*
*/
.mr-state-widget {
- background: $background-color;
+ background: $gray-light;
color: $gl-gray;
border: 1px solid $border-color;
border-radius: 2px;
@@ -359,7 +359,7 @@
th {
background-color: $white-light;
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
}
}
}
@@ -375,7 +375,7 @@
}
.mr-version-controls {
- background: $background-color;
+ background: $gray-light;
border-bottom: 1px solid $border-color;
color: $gl-text-color;
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index dfc6079bd15..77c523d7310 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -108,7 +108,7 @@
margin-top: 7px;
.issuable-number {
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
margin-right: 5px;
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index c35d71f9e7b..074abec7692 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -62,7 +62,7 @@
.common-note-form {
.md-area {
padding: $gl-padding-top $gl-padding;
- border: 1px solid $note-form-border-color;
+ border: 1px solid $border-color;
border-radius: $border-radius-base;
transition: border-color ease-in-out 0.15s,
box-shadow ease-in-out 0.15s;
@@ -204,7 +204,7 @@
.comment-toolbar {
padding-top: $gl-padding-top;
- color: $note-toolbar-color;
+ color: $gl-gray-light;
border-top: 1px solid $border-color;
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 10eb3d4203e..1b83b40486e 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -166,7 +166,7 @@ ul.notes {
.note {
display: block;
position: relative;
- border-bottom: 1px solid $table-border-gray;
+ border-bottom: 1px solid $white-normal;
&.note-discussion {
&.timeline-entry {
@@ -291,14 +291,14 @@ ul.notes {
font-family: $regular_font;
td {
- border: 1px solid $table-border-gray;
+ border: 1px solid $white-normal;
border-left: none;
&.notes_line {
vertical-align: middle;
text-align: center;
padding: 10px 0;
- background: $background-color;
+ background: $gray-light;
color: $text-color;
}
@@ -309,7 +309,7 @@ ul.notes {
}
&.notes_content {
- background-color: $background-color;
+ background-color: $gray-light;
border-width: 1px 0;
padding: 0;
vertical-align: top;
@@ -372,7 +372,7 @@ ul.notes {
.note-actions {
float: right;
margin-left: 10px;
- color: $notes-action-color;
+ color: $gray-darkest;
}
.note-actions {
@@ -411,7 +411,7 @@ ul.notes {
}
.fa {
- color: $notes-action-color;
+ color: $gray-darkest;
position: relative;
font-size: 17px;
}
@@ -448,7 +448,7 @@ ul.notes {
color: $notes-role-color;
font-size: 12px;
line-height: 20px;
- border: 1px solid $notes-role-border-color;
+ border: 1px solid $border-color;
border-radius: $border-radius-base;
}
@@ -529,7 +529,7 @@ ul.notes {
.line-resolve-all {
display: inline-block;
padding: 5px 10px;
- background-color: $background-color;
+ background-color: $gray-light;
border: 1px solid $border-color;
border-radius: $border-radius-default;
@@ -573,10 +573,10 @@ ul.notes {
svg {
position: relative;
- color: $notes-action-color;
+ color: $gray-darkest;
path {
- fill: $notes-action-color;
+ fill: $gray-darkest;
}
}
}
diff --git a/app/assets/stylesheets/pages/notifications.scss b/app/assets/stylesheets/pages/notifications.scss
index 7d61390a439..bdf07a99daf 100644
--- a/app/assets/stylesheets/pages/notifications.scss
+++ b/app/assets/stylesheets/pages/notifications.scss
@@ -10,19 +10,7 @@
position: relative;
top: 1px;
- > .fa {
+ .fa {
font-size: 18px;
}
}
-
-.ns-part {
- color: $gl-text-green;
-}
-
-.ns-watch {
- color: $gl-success;
-}
-
-.ns-mute {
- color: $gl-danger;
-}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 6822f916cc5..d5f9a7088be 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -118,7 +118,7 @@
height: 14px;
width: 14px;
vertical-align: middle;
- fill: $table-text-gray;
+ fill: $gl-gray-light;
}
.fa {
@@ -201,7 +201,7 @@
.duration,
.finished-at {
- color: $table-text-gray;
+ color: $gl-gray-light;
margin: 4px 0;
.fa {
@@ -222,7 +222,7 @@
.btn {
margin: 0;
- color: $table-text-gray;
+ color: $gl-gray-light;
}
.cancel-retry-btns {
@@ -235,10 +235,10 @@
.dropdown-toggle,
.dropdown-menu {
- color: $table-text-gray;
+ color: $gl-gray-light;
.fa {
- color: $table-text-gray;
+ color: $gl-gray-light;
font-size: 14px;
}
@@ -290,7 +290,7 @@
// Pipeline visualization
.toggle-pipeline-btn {
- background-color: $gray-dark;
+ background-color: $white-normal;
&.graph-collapsed {
background-color: $white-light;
@@ -299,7 +299,7 @@
.pipeline-graph {
width: 100%;
- background-color: $background-color;
+ background-color: $gray-light;
padding: $gl-padding;
overflow: auto;
white-space: nowrap;
@@ -655,7 +655,7 @@
.toggle-pipeline-btn {
.fa {
- color: $dropdown-header-color;
+ color: $gl-gray-light;
}
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index f8677f93fe0..8a5b0e20a86 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -60,8 +60,8 @@
.account-well {
padding: 10px;
- background-color: $help-well-bg;
- border: 1px solid $help-well-border;
+ background-color: $gray-light;
+ border: 1px solid $border-color;
border-radius: $border-radius-base;
ul {
@@ -136,7 +136,7 @@
.provider-btn-group {
display: inline-block;
margin-right: 10px;
- border: 1px solid $provider-btn-group-border;
+ border: 1px solid $border-color;
border-radius: 3px;
&:last-child {
@@ -147,7 +147,7 @@
.provider-btn-image {
display: inline-block;
padding: 5px 10px;
- border-right: 1px solid $provider-btn-group-border;
+ border-right: 1px solid $border-color;
> img {
width: 20px;
@@ -198,7 +198,7 @@
}
.personal-access-tokens-never-expires-label {
- color: $personal-access-tokens-disabled-label-color;
+ color: $note-disabled-comment-color;
}
.datepicker.personal-access-tokens-expires-at .ui-state-disabled span {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 6e0f6b1cd81..9c3dbc58ae0 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -420,13 +420,13 @@ a.deploy-project-label {
width: 100px;
height: 100px;
background-color: $gray-light;
- border: 1px solid $gray-dark;
+ border: 1px solid $white-normal;
margin: 0 auto;
border-radius: 50%;
i {
font-size: 100px;
- color: $gray-dark;
+ color: $white-normal;
}
}
@@ -536,7 +536,7 @@ a.deploy-project-label {
}
li.missing {
- border: 1px dashed $border-gray-light;
+ border: 1px dashed $border-gray-normal;
border-radius: $border-radius-default;
a {
@@ -591,7 +591,7 @@ pre.light-well {
@include basic-list;
.project-row {
- border-color: $table-border-color;
+ border-color: $white-normal;
.project-full-name {
@include str-truncated;
@@ -643,7 +643,7 @@ pre.light-well {
&.container-fluid {
padding-top: 12px;
padding-bottom: 12px;
- background-color: $background-color;
+ background-color: $gray-light;
border: 1px solid $border-color;
border-right-width: 0;
border-left-width: 0;
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 63d0a34e610..cedd4cb2987 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -51,9 +51,9 @@
border-radius: $border-radius-default;
font-size: 14px;
font-style: normal;
- color: $location-badge-color;
+ color: $note-disabled-comment-color;
display: inline-block;
- background-color: $location-badge-bg;
+ background-color: $gray-normal;
vertical-align: top;
cursor: default;
}
@@ -140,7 +140,7 @@
.search-input-wrap {
i {
- color: $location-icon-active-color;
+ color: $layout-link-gray;
}
}
}
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 51c926608f9..ddee2c95247 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -1,5 +1,5 @@
.settings-list-icon {
- color: $gl-placeholder-color;
+ color: $gl-gray-light;
font-size: $settings-icon-size;
line-height: 42px;
}
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 5084b466722..f3b0608e545 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -102,15 +102,15 @@
&.ci-created,
&.ci-skipped {
- color: $table-text-gray;
- border-color: $table-text-gray;
+ color: $gl-gray-light;
+ border-color: $gl-gray-light;
&:not(span):hover {
- background-color: rgba( $table-text-gray, .07);
+ background-color: rgba( $gl-gray-light, .07);
}
svg {
- fill: $table-text-gray;
+ fill: $gl-gray-light;
}
}
}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 20ad63be045..c0341db7289 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -20,8 +20,8 @@
margin-bottom: 0;
tr {
- border-bottom: 1px solid $table-border-gray;
- border-top: 1px solid $table-border-gray;
+ border-bottom: 1px solid $white-normal;
+ border-top: 1px solid $white-normal;
td,
th {
@@ -39,7 +39,7 @@
.commit-history-link-spacer {
margin: 0 10px;
- color: $table-border-color;
+ color: $white-normal;
}
&:hover {
@@ -53,7 +53,7 @@
&.selected {
td {
- background: $gray-dark;
+ background: $white-normal;
border-top: 1px solid $border-gray-dark;
border-bottom: 1px solid $border-gray-dark;
}
@@ -134,7 +134,7 @@
.blob-commit-info {
list-style: none;
padding: $gl-padding;
- background: $background-color;
+ background: $gray-light;
border: 1px solid $border-color;
border-bottom: none;
margin: 0;
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
index 9f9d630978a..b085c56390d 100644
--- a/app/assets/stylesheets/pages/xterm.scss
+++ b/app/assets/stylesheets/pages/xterm.scss
@@ -18,7 +18,7 @@
$l-blue: #81a2be;
$l-magenta: #b294bb;
$l-cyan: #8abeb7;
- $l-white: $ci-text-color;
+ $l-white: $gray-darkest;
/*
* xterm colors
diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb
index c33d7eecb9f..549a8526715 100644
--- a/app/controllers/concerns/service_params.rb
+++ b/app/controllers/concerns/service_params.rb
@@ -18,7 +18,7 @@ module ServiceParams
:add_pusher, :send_from_committer_email, :disable_diffs,
:external_wiki_url, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
- :jira_issue_transition_id, :url, :project_key]
+ :jira_issue_transition_id, :url, :project_key, :ca_pem, :namespace]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 15ca080c696..b71509f2c9b 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -217,6 +217,6 @@ class Projects::NotesController < Projects::ApplicationController
end
def find_current_user_notes
- @notes = NotesFinder.new.execute(project, current_user, params)
+ @notes = NotesFinder.new(project, current_user, params).execute.inc_author
end
end
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index be00a219205..707eddd4d29 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -23,10 +23,26 @@ class IssuesFinder < IssuableFinder
private
def init_collection
- Issue.visible_to_user(current_user)
+ IssuesFinder.not_restricted_by_confidentiality(current_user)
end
def iid_pattern
@iid_pattern ||= %r{\A#{Regexp.escape(Issue.reference_prefix)}(?<iid>\d+)\z}
end
+
+ def self.not_restricted_by_confidentiality(user)
+ return Issue.where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
+
+ return Issue.all if user.admin?
+
+ Issue.where('
+ issues.confidential IS NULL
+ OR issues.confidential IS FALSE
+ OR (issues.confidential = TRUE
+ AND (issues.author_id = :user_id
+ OR issues.assignee_id = :user_id
+ OR issues.project_id IN(:project_ids)))',
+ user_id: user.id,
+ project_ids: user.authorized_projects(Gitlab::Access::REPORTER).select(:id))
+ end
end
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 2484339e3a4..4bd8c83081a 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -1,27 +1,102 @@
class NotesFinder
FETCH_OVERLAP = 5.seconds
- def execute(project, current_user, params)
- target_type = params[:target_type]
- target_id = params[:target_id]
- # Default to 0 to remain compatible with old clients
- last_fetched_at = Time.at(params.fetch(:last_fetched_at, 0).to_i)
-
- notes =
- case target_type
- when "commit"
- project.notes.for_commit_id(target_id).non_diff_notes
- when "issue"
- IssuesFinder.new(current_user, project_id: project.id).find(target_id).notes.inc_author
- when "merge_request"
- MergeRequestsFinder.new(current_user, project_id: project.id).find(target_id).mr_and_commit_notes.inc_author
- when "snippet", "project_snippet"
- project.snippets.find(target_id).notes
+ # Used to filter Notes
+ # When used with target_type and target_id this returns notes specifically for the controller
+ #
+ # Arguments:
+ # current_user - which user check authorizations with
+ # project - which project to look for notes on
+ # params:
+ # target_type: string
+ # target_id: integer
+ # last_fetched_at: time
+ # search: string
+ #
+ def initialize(project, current_user, params = {})
+ @project = project
+ @current_user = current_user
+ @params = params
+ init_collection
+ end
+
+ def execute
+ @notes = since_fetch_at(@params[:last_fetched_at]) if @params[:last_fetched_at]
+ @notes
+ end
+
+ private
+
+ def init_collection
+ if @params[:target_id]
+ @notes = on_target(@params[:target_type], @params[:target_id])
+ else
+ @notes = notes_of_any_type
+ end
+ end
+
+ def notes_of_any_type
+ types = %w(commit issue merge_request snippet)
+ note_relations = types.map { |t| notes_for_type(t) }
+ note_relations.map!{ |notes| search(@params[:search], notes) } if @params[:search]
+ UnionFinder.new.find_union(note_relations, Note)
+ end
+
+ def noteables_for_type(noteable_type)
+ case noteable_type
+ when "issue"
+ IssuesFinder.new(@current_user, project_id: @project.id).execute
+ when "merge_request"
+ MergeRequestsFinder.new(@current_user, project_id: @project.id).execute
+ when "snippet", "project_snippet"
+ SnippetsFinder.new.execute(@current_user, filter: :by_project, project: @project)
+ else
+ raise 'invalid target_type'
+ end
+ end
+
+ def notes_for_type(noteable_type)
+ if noteable_type == "commit"
+ if Ability.allowed?(@current_user, :download_code, @project)
+ @project.notes.where(noteable_type: 'Commit')
+ else
+ Note.none
+ end
+ else
+ finder = noteables_for_type(noteable_type)
+ @project.notes.where(noteable_type: finder.base_class.name, noteable_id: finder.reorder(nil))
+ end
+ end
+
+ def on_target(target_type, target_id)
+ if target_type == "commit"
+ notes_for_type('commit').for_commit_id(target_id)
+ else
+ target = noteables_for_type(target_type).find(target_id)
+
+ if target.respond_to?(:related_notes)
+ target.related_notes
else
- raise 'invalid target_type'
+ target.notes
end
+ end
+ end
+
+ # Searches for notes matching the given query.
+ #
+ # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
+ #
+ def search(query, notes_relation = @notes)
+ pattern = "%#{query}%"
+ notes_relation.where(Note.arel_table[:note].matches(pattern))
+ end
+
+ # Notes changed since last fetch
+ # Uses overlapping intervals to avoid worrying about race conditions
+ def since_fetch_at(fetch_time)
+ # Default to 0 to remain compatible with old clients
+ last_fetched_at = Time.at(@params.fetch(:last_fetched_at, 0).to_i)
- # Use overlapping intervals to avoid worrying about race conditions
- notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP).fresh
+ @notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP).fresh
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index e7cf606a7ae..fdbf28a1d68 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -9,6 +9,14 @@ module Ci
has_many :deployments, as: :deployable
+ # The "environment" field for builds is a String, and is the unexpanded name
+ def persisted_environment
+ @persisted_environment ||= Environment.find_by(
+ name: expanded_environment_name,
+ project_id: gl_project_id
+ )
+ end
+
serialize :options
serialize :yaml_variables
@@ -143,7 +151,7 @@ module Ci
end
def expanded_environment_name
- ExpandVariables.expand(environment, variables) if environment
+ ExpandVariables.expand(environment, simple_variables) if environment
end
def has_environment?
@@ -195,7 +203,19 @@ module Ci
project.build_timeout
end
- def variables
+ # A slugified version of the build ref, suitable for inclusion in URLs and
+ # domain names. Rules:
+ #
+ # * Lowercased
+ # * Anything not matching [a-z0-9-] is replaced with a -
+ # * Maximum length is 63 bytes
+ def ref_slug
+ slugified = ref.to_s.downcase
+ slugified.gsub(/[^a-z0-9]/, '-')[0..62]
+ end
+
+ # Variables whose value does not depend on other variables
+ def simple_variables
variables = predefined_variables
variables += project.predefined_variables
variables += pipeline.predefined_variables
@@ -208,6 +228,13 @@ module Ci
variables
end
+ # All variables, including those dependent on other variables
+ def variables
+ variables = simple_variables
+ variables += persisted_environment.predefined_variables if persisted_environment.present?
+ variables
+ end
+
def merge_request
merge_requests = MergeRequest.includes(:merge_request_diff)
.where(source_branch: ref, source_project_id: pipeline.gl_project_id)
@@ -529,6 +556,7 @@ module Ci
{ key: 'CI_BUILD_REF', value: sha, public: true },
{ key: 'CI_BUILD_BEFORE_SHA', value: before_sha, public: true },
{ key: 'CI_BUILD_REF_NAME', value: ref, public: true },
+ { key: 'CI_BUILD_REF_SLUG', value: ref_slug, public: true },
{ key: 'CI_BUILD_NAME', value: name, public: true },
{ key: 'CI_BUILD_STAGE', value: stage, public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 54f73171fd4..48354cdbefb 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -88,8 +88,24 @@ module Ci
end
# ref can't be HEAD or SHA, can only be branch/tag name
+ scope :latest, ->(ref = nil) do
+ max_id = unscope(:select)
+ .select("max(#{quoted_table_name}.id)")
+ .group(:ref, :sha)
+
+ if ref
+ where(id: max_id, ref: ref)
+ else
+ where(id: max_id)
+ end
+ end
+
+ def self.latest_status(ref = nil)
+ latest(ref).status
+ end
+
def self.latest_successful_for(ref)
- where(ref: ref).order(id: :desc).success.first
+ success.latest(ref).first
end
def self.truncate_sha(sha)
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 1831cc7e175..69cfc47f5bf 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -228,13 +228,9 @@ class Commit
def status(ref = nil)
@statuses ||= {}
- if @statuses.key?(ref)
- @statuses[ref]
- elsif ref
- @statuses[ref] = pipelines.where(ref: ref).status
- else
- @statuses[ref] = pipelines.status
- end
+ return @statuses[ref] if @statuses.key?(ref)
+
+ @statuses[ref] = pipelines.latest_status(ref)
end
def revert_branch_name
@@ -270,7 +266,7 @@ class Commit
@merged_merge_request_hash ||= Hash.new do |hash, user|
hash[user] = merged_merge_request_no_cache(user)
end
-
+
@merged_merge_request_hash[current_user]
end
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index 875e9834487..4359f1d7b06 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -30,7 +30,7 @@ module Milestoneish
end
def issues_visible_to_user(user)
- issues.visible_to_user(user)
+ IssuesFinder.new(user).execute.where(id: issues)
end
def upcoming?
diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb
index 04d30f46210..1ca7f91dc03 100644
--- a/app/models/concerns/token_authenticatable.rb
+++ b/app/models/concerns/token_authenticatable.rb
@@ -39,6 +39,10 @@ module TokenAuthenticatable
current_token.blank? ? write_new_token(token_field) : current_token
end
+ define_method("set_#{token_field}") do |token|
+ write_attribute(token_field, token) if token
+ end
+
define_method("ensure_#{token_field}!") do
send("reset_#{token_field}!") if read_attribute(token_field).blank?
read_attribute(token_field)
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 96700143ddd..8ef1c841ea3 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -1,9 +1,15 @@
class Environment < ActiveRecord::Base
+ # Used to generate random suffixes for the slug
+ NUMBERS = '0'..'9'
+ SUFFIX_CHARS = ('a'..'z').to_a + NUMBERS.to_a
+
belongs_to :project, required: true, validate: true
has_many :deployments
before_validation :nullify_external_url
+ before_validation :generate_slug, if: ->(env) { env.slug.blank? }
+
before_save :set_environment_type
validates :name,
@@ -13,6 +19,13 @@ class Environment < ActiveRecord::Base
format: { with: Gitlab::Regex.environment_name_regex,
message: Gitlab::Regex.environment_name_regex_message }
+ validates :slug,
+ presence: true,
+ uniqueness: { scope: :project_id },
+ length: { maximum: 24 },
+ format: { with: Gitlab::Regex.environment_slug_regex,
+ message: Gitlab::Regex.environment_slug_regex_message }
+
validates :external_url,
uniqueness: { scope: :project_id },
length: { maximum: 255 },
@@ -37,6 +50,13 @@ class Environment < ActiveRecord::Base
state :stopped
end
+ def predefined_variables
+ [
+ { key: 'CI_ENVIRONMENT_NAME', value: name, public: true },
+ { key: 'CI_ENVIRONMENT_SLUG', value: slug, public: true },
+ ]
+ end
+
def recently_updated_on_branch?(ref)
ref.to_s == last_deployment.try(:ref)
end
@@ -107,4 +127,41 @@ class Environment < ActiveRecord::Base
action.expanded_environment_name == environment
end
end
+
+ # An environment name is not necessarily suitable for use in URLs, DNS
+ # or other third-party contexts, so provide a slugified version. A slug has
+ # the following properties:
+ # * contains only lowercase letters (a-z), numbers (0-9), and '-'
+ # * begins with a letter
+ # * has a maximum length of 24 bytes (OpenShift limitation)
+ # * cannot end with `-`
+ def generate_slug
+ # Lowercase letters and numbers only
+ slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-')
+
+ # Must start with a letter
+ slugified = "env-" + slugified if NUMBERS.cover?(slugified[0])
+
+ # Maximum length: 24 characters (OpenShift limitation)
+ slugified = slugified[0..23]
+
+ # Cannot end with a "-" character (Kubernetes label limitation)
+ slugified = slugified[0..-2] if slugified[-1] == "-"
+
+ # Add a random suffix, shortening the current string if necessary, if it
+ # has been slugified. This ensures uniqueness.
+ slugified = slugified[0..16] + "-" + random_suffix if slugified != name
+
+ self.slug = slugified
+ end
+
+ private
+
+ # Slugifying a name may remove the uniqueness guarantee afforded by it being
+ # based on name (which must be unique). To compensate, we add a random
+ # 6-byte suffix in those circumstances. This is not *guaranteed* uniqueness,
+ # but the chance of collisions is vanishingly small
+ def random_suffix
+ (0..5).map { SUFFIX_CHARS.sample }.join
+ end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 7fe92051037..738c96e4db3 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -60,61 +60,6 @@ class Issue < ActiveRecord::Base
attributes
end
- class << self
- private
-
- # Returns the project that the current scope belongs to if any, nil otherwise.
- #
- # Examples:
- # - my_project.issues.without_due_date.owner_project => my_project
- # - Issue.all.owner_project => nil
- def owner_project
- # No owner if we're not being called from an association
- return unless all.respond_to?(:proxy_association)
-
- owner = all.proxy_association.owner
-
- # Check if the association is or belongs to a project
- if owner.is_a?(Project)
- owner
- else
- begin
- owner.association(:project).target
- rescue ActiveRecord::AssociationNotFoundError
- nil
- end
- end
- end
- end
-
- def self.visible_to_user(user)
- return where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
- return all if user.admin?
-
- # Check if we are scoped to a specific project's issues
- if owner_project
- if owner_project.team.member?(user, Gitlab::Access::REPORTER)
- # If the project is authorized for the user, they can see all issues in the project
- return all
- else
- # else only non confidential and authored/assigned to them
- return where('issues.confidential IS NULL OR issues.confidential IS FALSE
- OR issues.author_id = :user_id OR issues.assignee_id = :user_id',
- user_id: user.id)
- end
- end
-
- where('
- issues.confidential IS NULL
- OR issues.confidential IS FALSE
- OR (issues.confidential = TRUE
- AND (issues.author_id = :user_id
- OR issues.assignee_id = :user_id
- OR issues.project_id IN(:project_ids)))',
- user_id: user.id,
- project_ids: user.authorized_projects(Gitlab::Access::REPORTER).select(:id))
- end
-
def self.reference_prefix
'#'
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index ea3cf1cdaac..b73d7acefea 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -452,7 +452,7 @@ class MergeRequest < ActiveRecord::Base
should_remove_source_branch? || force_remove_source_branch?
end
- def mr_and_commit_notes
+ def related_notes
# Fetch comments only from last 100 commits
commits_for_notes_limit = 100
commit_ids = commits.last(commits_for_notes_limit).map(&:id)
@@ -468,7 +468,7 @@ class MergeRequest < ActiveRecord::Base
end
def discussions
- @discussions ||= self.mr_and_commit_notes.
+ @discussions ||= self.related_notes.
inc_relations_for_view.
fresh.
discussions
diff --git a/app/models/note.rb b/app/models/note.rb
index 08bd08743ef..0c1b05dabf2 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -107,23 +107,6 @@ class Note < ActiveRecord::Base
Discussion.for_diff_notes(active_notes).
map { |d| [d.line_code, d] }.to_h
end
-
- # Searches for notes matching the given query.
- #
- # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
- #
- # query - The search query as a String.
- # as_user - Limit results to those viewable by a specific user
- #
- # Returns an ActiveRecord::Relation.
- def search(query, as_user: nil)
- table = arel_table
- pattern = "%#{query}%"
-
- Note.joins('LEFT JOIN issues ON issues.id = noteable_id').
- where(table[:note].matches(pattern)).
- merge(Issue.visible_to_user(as_user))
- end
end
def cross_reference?
diff --git a/app/models/project.rb b/app/models/project.rb
index 77d740081c6..2c726cfc5df 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -106,6 +106,7 @@ class Project < ActiveRecord::Base
has_one :bugzilla_service, dependent: :destroy
has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
has_one :external_wiki_service, dependent: :destroy
+ has_one :kubernetes_service, dependent: :destroy, inverse_of: :project
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
@@ -742,6 +743,14 @@ class Project < ActiveRecord::Base
@ci_service ||= ci_services.reorder(nil).find_by(active: true)
end
+ def deployment_services
+ services.where(category: :deployment)
+ end
+
+ def deployment_service
+ @deployment_service ||= deployment_services.reorder(nil).find_by(active: true)
+ end
+
def jira_tracker?
issues_tracker.to_param == 'jira'
end
diff --git a/app/models/project_services/deployment_service.rb b/app/models/project_services/deployment_service.rb
new file mode 100644
index 00000000000..55e98c31251
--- /dev/null
+++ b/app/models/project_services/deployment_service.rb
@@ -0,0 +1,11 @@
+# Base class for deployment services
+#
+# These services integrate with a deployment solution like Kubernetes/OpenShift,
+# Mesosphere, etc, to provide additional features to environments.
+class DeploymentService < Service
+ default_value_for :category, 'deployment'
+
+ def supported_events
+ []
+ end
+end
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
new file mode 100644
index 00000000000..80ae1191108
--- /dev/null
+++ b/app/models/project_services/kubernetes_service.rb
@@ -0,0 +1,118 @@
+class KubernetesService < DeploymentService
+ # Namespace defaults to the project path, but can be overridden in case that
+ # is an invalid or inappropriate name
+ prop_accessor :namespace
+
+ # Access to kubernetes is directly through the API
+ prop_accessor :api_url
+
+ # Bearer authentication
+ # TODO: user/password auth, client certificates
+ prop_accessor :token
+
+ # Provide a custom CA bundle for self-signed deployments
+ prop_accessor :ca_pem
+
+ with_options presence: true, if: :activated? do
+ validates :api_url, url: true
+ validates :token
+
+ validates :namespace,
+ format: {
+ with: Gitlab::Regex.kubernetes_namespace_regex,
+ message: Gitlab::Regex.kubernetes_namespace_regex_message,
+ },
+ length: 1..63
+ end
+
+ def initialize_properties
+ if properties.nil?
+ self.properties = {}
+ self.namespace = project.path if project.present?
+ end
+ end
+
+ def title
+ 'Kubernetes'
+ end
+
+ def description
+ 'Kubernetes / Openshift integration'
+ end
+
+ def help
+ ''
+ end
+
+ def to_param
+ 'kubernetes'
+ end
+
+ def fields
+ [
+ { type: 'text',
+ name: 'namespace',
+ title: 'Kubernetes namespace',
+ placeholder: 'Kubernetes namespace',
+ },
+ { type: 'text',
+ name: 'api_url',
+ title: 'API URL',
+ placeholder: 'Kubernetes API URL, like https://kube.example.com/',
+ },
+ { type: 'text',
+ name: 'token',
+ title: 'Service token',
+ placeholder: 'Service token',
+ },
+ { type: 'textarea',
+ name: 'ca_pem',
+ title: 'Custom CA bundle',
+ placeholder: 'Certificate Authority bundle (PEM format)',
+ },
+ ]
+ end
+
+ # Check we can connect to the Kubernetes API
+ def test(*args)
+ kubeclient = build_kubeclient
+ kubeclient.discover
+
+ { success: kubeclient.discovered, result: "Checked API discovery endpoint" }
+ rescue => err
+ { success: false, result: err }
+ end
+
+ private
+
+ def build_kubeclient(api_path = '/api', api_version = 'v1')
+ return nil unless api_url && namespace && token
+
+ url = URI.parse(api_url)
+ url.path = url.path[0..-2] if url.path[-1] == "/"
+ url.path += api_path
+
+ ::Kubeclient::Client.new(
+ url,
+ api_version,
+ ssl_options: kubeclient_ssl_options,
+ auth_options: kubeclient_auth_options,
+ http_proxy_uri: ENV['http_proxy']
+ )
+ end
+
+ def kubeclient_ssl_options
+ opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
+
+ if ca_pem.present?
+ opts[:cert_store] = OpenSSL::X509::Store.new
+ opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
+ end
+
+ opts
+ end
+
+ def kubeclient_auth_options
+ { bearer_token: token }
+ end
+end
diff --git a/app/models/project_services/slack_service/pipeline_message.rb b/app/models/project_services/slack_service/pipeline_message.rb
index f8d03c0e2fa..b6355fc4171 100644
--- a/app/models/project_services/slack_service/pipeline_message.rb
+++ b/app/models/project_services/slack_service/pipeline_message.rb
@@ -13,7 +13,7 @@ class SlackService
@project_name = data[:project][:path_with_namespace]
@project_url = data[:project][:web_url]
- @user_name = data[:user] && data[:user][:name]
+ @user_name = (data[:user] && data[:user][:name]) || 'API'
end
def pretext
diff --git a/app/models/service.rb b/app/models/service.rb
index 0c36acfc1b7..e49a8fa2904 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -214,6 +214,7 @@ class Service < ActiveRecord::Base
hipchat
irker
jira
+ kubernetes
mattermost_slash_commands
pipelines_email
pivotaltracker
diff --git a/app/services/ci/create_pipeline_builds_service.rb b/app/services/ci/create_pipeline_builds_service.rb
index 005014fa1de..b7da3f8e7eb 100644
--- a/app/services/ci/create_pipeline_builds_service.rb
+++ b/app/services/ci/create_pipeline_builds_service.rb
@@ -10,18 +10,29 @@ module Ci
end
end
+ def project
+ pipeline.project
+ end
+
private
def create_build(build_attributes)
build_attributes = build_attributes.merge(
pipeline: pipeline,
- project: pipeline.project,
+ project: project,
ref: pipeline.ref,
tag: pipeline.tag,
user: current_user,
trigger_request: trigger_request
)
- pipeline.builds.create(build_attributes)
+ build = pipeline.builds.create(build_attributes)
+
+ # Create the environment before the build starts. This sets its slug and
+ # makes it available as an environment variable
+ project.environments.find_or_create_by(name: build.expanded_environment_name) if
+ build.has_environment?
+
+ build
end
def new_builds
diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb
index 75d847d5bee..240ddabec36 100644
--- a/app/services/ci/image_for_build_service.rb
+++ b/app/services/ci/image_for_build_service.rb
@@ -1,13 +1,13 @@
module Ci
class ImageForBuildService
def execute(project, opts)
- sha = opts[:sha] || ref_sha(project, opts[:ref])
-
+ ref = opts[:ref]
+ sha = opts[:sha] || ref_sha(project, ref)
pipelines = project.pipelines.where(sha: sha)
- pipelines = pipelines.where(ref: opts[:ref]) if opts[:ref]
- image_name = image_for_status(pipelines.status)
+ image_name = image_for_status(pipelines.latest_status(ref))
image_path = Rails.root.join('public/ci', image_name)
+
OpenStruct.new(path: image_path, name: image_name)
end
diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb
index b6c52ddac7a..86f317dcd18 100644
--- a/app/uploaders/artifact_uploader.rb
+++ b/app/uploaders/artifact_uploader.rb
@@ -1,4 +1,4 @@
-class ArtifactUploader < CarrierWave::Uploader::Base
+class ArtifactUploader < GitlabUploader
storage :file
attr_accessor :build, :field
@@ -38,12 +38,4 @@ class ArtifactUploader < CarrierWave::Uploader::Base
def exists?
file.try(:exists?)
end
-
- def move_to_cache
- true
- end
-
- def move_to_store
- true
- end
end
diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb
index fb3b5dfecd0..cfcb877cc3e 100644
--- a/app/uploaders/attachment_uploader.rb
+++ b/app/uploaders/attachment_uploader.rb
@@ -1,4 +1,4 @@
-class AttachmentUploader < CarrierWave::Uploader::Base
+class AttachmentUploader < GitlabUploader
include UploaderHelper
storage :file
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index 38683fdf6d7..a1ecb7bc00b 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -1,4 +1,4 @@
-class AvatarUploader < CarrierWave::Uploader::Base
+class AvatarUploader < GitlabUploader
include UploaderHelper
storage :file
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 3ac6030c21c..47bef7cd1e4 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -1,4 +1,4 @@
-class FileUploader < CarrierWave::Uploader::Base
+class FileUploader < GitlabUploader
include UploaderHelper
MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
new file mode 100644
index 00000000000..02d7c601d6c
--- /dev/null
+++ b/app/uploaders/gitlab_uploader.rb
@@ -0,0 +1,11 @@
+class GitlabUploader < CarrierWave::Uploader::Base
+ # Reduce disk IO
+ def move_to_cache
+ true
+ end
+
+ # Reduce disk IO
+ def move_to_store
+ true
+ end
+end
diff --git a/app/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb
index 4f356dd663e..faab539b8e0 100644
--- a/app/uploaders/lfs_object_uploader.rb
+++ b/app/uploaders/lfs_object_uploader.rb
@@ -1,4 +1,4 @@
-class LfsObjectUploader < CarrierWave::Uploader::Base
+class LfsObjectUploader < GitlabUploader
storage :file
def store_dir
@@ -9,14 +9,6 @@ class LfsObjectUploader < CarrierWave::Uploader::Base
"#{Gitlab.config.lfs.storage_path}/tmp/cache"
end
- def move_to_cache
- true
- end
-
- def move_to_store
- true
- end
-
def exists?
file.try(:exists?)
end
diff --git a/app/views/explore/_head.html.haml b/app/views/explore/_head.html.haml
index d8a57560788..a3b0709e261 100644
--- a/app/views/explore/_head.html.haml
+++ b/app/views/explore/_head.html.haml
@@ -1,5 +1,5 @@
-.explore-title
- %h3
+.explore-title.text-center
+ %h2
Explore GitLab
%p.lead
Discover projects, groups and snippets. Share your projects with others
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index fa189ae62d8..959c796ecec 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -39,7 +39,7 @@
= icon('thumbs-down')
= downvotes
- - note_count = merge_request.mr_and_commit_notes.user.count
+ - note_count = merge_request.related_notes.user.count
%li
= link_to merge_request_path(merge_request, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
= icon('comments')
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 896f10104fa..5febe806bdd 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -53,7 +53,7 @@
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
Discussion
- %span.badge= @merge_request.mr_and_commit_notes.user.count
+ %span.badge= @merge_request.related_notes.user.count
- if @merge_request.source_project
%li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml
index a0e12fb3f38..baa1ade5eee 100644
--- a/app/views/projects/merge_requests/show/_commits.html.haml
+++ b/app/views/projects/merge_requests/show/_commits.html.haml
@@ -1,6 +1,2 @@
-.content-block.oneline-block
- = icon("sort-amount-desc")
- Most recent commits displayed first
-
%ol#commits-list.list-unstyled
= render "projects/commits/commits", project: @merge_request.source_project, ref: @merge_request.source_branch
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index 9503dbded13..79d87b7db12 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -8,10 +8,11 @@
= blob_icon 0, @snippet.file_name
= @snippet.file_name
.file-actions
- = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
+ = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']", class: "btn btn-sm")
= link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
= render 'shared/snippets/blob'
- = render 'award_emoji/awards_block', awardable: @snippet, inline: true
+ .row-content-block.top-block.content-component-block
+ = render 'award_emoji/awards_block', awardable: @snippet, inline: true
%div#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 958f8413e1d..9fe1be5a597 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -167,4 +167,4 @@
new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}');
gl.Subscription.bindAll('.subscription');
new gl.DueDateSelectors();
- sidebar = new Sidebar();
+ window.sidebar = new Sidebar();
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 27d7a6c5bb6..837a1a0cc8c 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -7,9 +7,9 @@
= blob_icon 0, @snippet.file_name
= @snippet.file_name
.file-actions
- = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
+ = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']", class: "btn btn-sm")
= link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
= link_to 'Download', download_snippet_path(@snippet), class: "btn btn-sm"
= render 'shared/snippets/blob'
-= render 'award_emoji/awards_block', awardable: @snippet, inline: true \ No newline at end of file
+= render 'award_emoji/awards_block', awardable: @snippet, inline: true
diff --git a/changelogs/unreleased/22849-ci-build-ref-slug.yml b/changelogs/unreleased/22849-ci-build-ref-slug.yml
new file mode 100644
index 00000000000..b159ecca6d8
--- /dev/null
+++ b/changelogs/unreleased/22849-ci-build-ref-slug.yml
@@ -0,0 +1,4 @@
+---
+title: Introduce $CI_BUILD_REF_SLUG
+merge_request: 8072
+author:
diff --git a/changelogs/unreleased/22864-add-environment-slug.yml b/changelogs/unreleased/22864-add-environment-slug.yml
new file mode 100644
index 00000000000..f90f79337d5
--- /dev/null
+++ b/changelogs/unreleased/22864-add-environment-slug.yml
@@ -0,0 +1,4 @@
+---
+title: Add a slug to environments
+merge_request: 7983
+author:
diff --git a/changelogs/unreleased/22864-kubernetes-service.yml b/changelogs/unreleased/22864-kubernetes-service.yml
new file mode 100644
index 00000000000..ea1323cbeb0
--- /dev/null
+++ b/changelogs/unreleased/22864-kubernetes-service.yml
@@ -0,0 +1,4 @@
+---
+title: Introduce deployment services, starting with a KubernetesService
+merge_request: 7994
+author:
diff --git a/changelogs/unreleased/24824-dropdown-items-focus.yml b/changelogs/unreleased/24824-dropdown-items-focus.yml
new file mode 100644
index 00000000000..66970c2a9a5
--- /dev/null
+++ b/changelogs/unreleased/24824-dropdown-items-focus.yml
@@ -0,0 +1,4 @@
+---
+title: Add focus state to dropdown items
+merge_request:
+author:
diff --git a/changelogs/unreleased/25534-adding-a-way-to-go-back-on-error-pages.yml b/changelogs/unreleased/25534-adding-a-way-to-go-back-on-error-pages.yml
new file mode 100644
index 00000000000..c6a92547c5c
--- /dev/null
+++ b/changelogs/unreleased/25534-adding-a-way-to-go-back-on-error-pages.yml
@@ -0,0 +1,4 @@
+---
+title: Added go back anchor on error pages.
+merge_request: 8087
+author:
diff --git a/changelogs/unreleased/8003-katex-math.yml b/changelogs/unreleased/8003-katex-math.yml
new file mode 100644
index 00000000000..a40dcde1393
--- /dev/null
+++ b/changelogs/unreleased/8003-katex-math.yml
@@ -0,0 +1,4 @@
+---
+title: Added support for math rendering, using KaTeX, in Markdown and asciidoc
+merge_request: 8003
+author: Munken
diff --git a/changelogs/unreleased/file-template-dropwdown-proper-position.yml b/changelogs/unreleased/file-template-dropwdown-proper-position.yml
new file mode 100644
index 00000000000..cf2a622b7e6
--- /dev/null
+++ b/changelogs/unreleased/file-template-dropwdown-proper-position.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed file template dropdown for the "New File" editor for smaller/zoomed screens
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-slack-pipeline-message-by-api.yml b/changelogs/unreleased/fix-slack-pipeline-message-by-api.yml
new file mode 100644
index 00000000000..aa5ad5cd8d6
--- /dev/null
+++ b/changelogs/unreleased/fix-slack-pipeline-message-by-api.yml
@@ -0,0 +1,4 @@
+---
+title: Fix Slack pipeline message from pipelines made by API
+merge_request: 8059
+author:
diff --git a/changelogs/unreleased/gitlab-workhorse-multipart.yml b/changelogs/unreleased/gitlab-workhorse-multipart.yml
new file mode 100644
index 00000000000..23c2139cf93
--- /dev/null
+++ b/changelogs/unreleased/gitlab-workhorse-multipart.yml
@@ -0,0 +1,4 @@
+---
+title: Replace Rack::Multipart with GitLab-Workhorse based solution
+merge_request: 5867
+author:
diff --git a/changelogs/unreleased/jej-24637-move-issue-visible_to_user-to-finder.yml b/changelogs/unreleased/jej-24637-move-issue-visible_to_user-to-finder.yml
new file mode 100644
index 00000000000..db1389e2024
--- /dev/null
+++ b/changelogs/unreleased/jej-24637-move-issue-visible_to_user-to-finder.yml
@@ -0,0 +1,4 @@
+---
+title: Issue#visible_to_user moved to IssuesFinder to prevent accidental use
+merge_request:
+author:
diff --git a/changelogs/unreleased/jej-note-search-uses-finder.yml b/changelogs/unreleased/jej-note-search-uses-finder.yml
new file mode 100644
index 00000000000..1768bdfd487
--- /dev/null
+++ b/changelogs/unreleased/jej-note-search-uses-finder.yml
@@ -0,0 +1,4 @@
+---
+title: Fix missing Note access checks by moving Note#search to updated NoteFinder
+merge_request:
+author:
diff --git a/changelogs/unreleased/move-admin-active-tab-spinach-tests-to-rspec.yml b/changelogs/unreleased/move-admin-active-tab-spinach-tests-to-rspec.yml
new file mode 100644
index 00000000000..11250643a23
--- /dev/null
+++ b/changelogs/unreleased/move-admin-active-tab-spinach-tests-to-rspec.yml
@@ -0,0 +1,4 @@
+---
+title: Move admin active tab spinach tests to rspec
+merge_request: 8037
+author: Semyon Pupkov
diff --git a/changelogs/unreleased/remove-unnecessary-message-mr-commits-tab.yml b/changelogs/unreleased/remove-unnecessary-message-mr-commits-tab.yml
new file mode 100644
index 00000000000..754af641add
--- /dev/null
+++ b/changelogs/unreleased/remove-unnecessary-message-mr-commits-tab.yml
@@ -0,0 +1,4 @@
+---
+title: Remove unnecessary commits order message
+merge_request: 8004
+author:
diff --git a/changelogs/unreleased/seed-runner-token.yml b/changelogs/unreleased/seed-runner-token.yml
new file mode 100644
index 00000000000..e8153be043a
--- /dev/null
+++ b/changelogs/unreleased/seed-runner-token.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for setting the GitLab Runners Registration Token during initial
+ database seeding
+merge_request: 6642
+author:
diff --git a/changelogs/unreleased/show-commit-status-from-latest-pipeline.yml b/changelogs/unreleased/show-commit-status-from-latest-pipeline.yml
new file mode 100644
index 00000000000..bbd7a217493
--- /dev/null
+++ b/changelogs/unreleased/show-commit-status-from-latest-pipeline.yml
@@ -0,0 +1,4 @@
+---
+title: Show commit status from latest pipeline
+merge_request: 7333
+author:
diff --git a/changelogs/unreleased/username-exists-root.yml b/changelogs/unreleased/username-exists-root.yml
new file mode 100644
index 00000000000..1ffb3eb435c
--- /dev/null
+++ b/changelogs/unreleased/username-exists-root.yml
@@ -0,0 +1,4 @@
+---
+title: Username exists check respects relative root path
+merge_request:
+author:
diff --git a/config/application.rb b/config/application.rb
index 0aa2873f94a..782a7a36895 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -45,7 +45,7 @@ module Gitlab
#
# Parameters filtered:
# - Password (:password, :password_confirmation)
- # - Private tokens (:private_token, :authentication_token)
+ # - Private tokens
# - Two-factor tokens (:otp_attempt)
# - Repo/Project Import URLs (:import_url)
# - Build variables (:variables)
@@ -60,11 +60,13 @@ module Gitlab
encrypted_key
hook
import_url
+ incoming_email_token
key
otp_attempt
password
password_confirmation
private_token
+ runners_token
secret_token
sentry_dsn
variables
@@ -85,6 +87,8 @@ module Gitlab
config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
+ config.assets.precompile << "katex.css"
+ config.assets.precompile << "katex.js"
config.assets.precompile << "graphs/graphs_bundle.js"
config.assets.precompile << "users/users_bundle.js"
config.assets.precompile << "network/network_bundle.js"
diff --git a/config/initializers/math_lexer.rb b/config/initializers/math_lexer.rb
new file mode 100644
index 00000000000..8a3388a267e
--- /dev/null
+++ b/config/initializers/math_lexer.rb
@@ -0,0 +1,2 @@
+# Touch the lexers so it is registered with Rouge
+Rouge::Lexers::Math
diff --git a/config/initializers/workhorse_multipart.rb b/config/initializers/workhorse_multipart.rb
new file mode 100644
index 00000000000..3e2f25c354a
--- /dev/null
+++ b/config/initializers/workhorse_multipart.rb
@@ -0,0 +1,3 @@
+Rails.application.configure do |config|
+ config.middleware.use(Gitlab::Middleware::Multipart)
+end
diff --git a/db/fixtures/production/010_settings.rb b/db/fixtures/production/010_settings.rb
new file mode 100644
index 00000000000..5522f31629a
--- /dev/null
+++ b/db/fixtures/production/010_settings.rb
@@ -0,0 +1,16 @@
+if ENV['GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN'].present?
+ settings = ApplicationSetting.current || ApplicationSetting.create_from_defaults
+ settings.set_runners_registration_token(ENV['GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN'])
+
+ if settings.save
+ puts "Saved Runner Registration Token".color(:green)
+ else
+ puts "Could not save Runner Registration Token".color(:red)
+ puts
+ settings.errors.full_messages.map do |message|
+ puts "--> #{message}".color(:red)
+ end
+ puts
+ exit 1
+ end
+end
diff --git a/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb b/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb
new file mode 100644
index 00000000000..b74552e762d
--- /dev/null
+++ b/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb
@@ -0,0 +1,53 @@
+class FixupEnvironmentNameUniqueness < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+ DOWNTIME_REASON = 'Renaming non-unique environments'
+
+ def up
+ environments = Arel::Table.new(:environments)
+
+ # Get all [project_id, name] pairs that occur more than once
+ finder_sql = environments.
+ group(environments[:project_id], environments[:name]).
+ having(Arel.sql("COUNT(1)").gt(1)).
+ project(environments[:project_id], environments[:name]).
+ to_sql
+
+ conflicting = connection.exec_query(finder_sql)
+
+ conflicting.rows.each do |project_id, name|
+ fix_duplicates(project_id, name)
+ end
+ end
+
+ def down
+ # Nothing to do
+ end
+
+ # Rename conflicting environments by appending "-#{id}" to all but the first
+ def fix_duplicates(project_id, name)
+ environments = Arel::Table.new(:environments)
+ finder_sql = environments.
+ where(environments[:project_id].eq(project_id)).
+ where(environments[:name].eq(name)).
+ order(environments[:id].asc).
+ project(environments[:id], environments[:name]).
+ to_sql
+
+ # Now we have the data for all the conflicting rows
+ conflicts = connection.exec_query(finder_sql).rows
+ conflicts.shift # Leave the first row alone
+
+ conflicts.each do |id, name|
+ update_sql =
+ Arel::UpdateManager.new(ActiveRecord::Base).
+ table(environments).
+ set(environments[:name] => name + "-" + id.to_s).
+ where(environments[:id].eq(id)).
+ to_sql
+
+ connection.exec_update(update_sql, self.class.name, [])
+ end
+ end
+end
diff --git a/db/migrate/20161207231621_create_environment_name_unique_index.rb b/db/migrate/20161207231621_create_environment_name_unique_index.rb
new file mode 100644
index 00000000000..ac680c8d10f
--- /dev/null
+++ b/db/migrate/20161207231621_create_environment_name_unique_index.rb
@@ -0,0 +1,18 @@
+class CreateEnvironmentNameUniqueIndex < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = true
+ DOWNTIME_REASON = 'Making a non-unique index into a unique index'
+
+ def up
+ remove_index :environments, [:project_id, :name]
+ add_concurrent_index :environments, [:project_id, :name], unique: true
+ end
+
+ def down
+ remove_index :environments, [:project_id, :name], unique: true
+ add_concurrent_index :environments, [:project_id, :name]
+ end
+end
diff --git a/db/migrate/20161207231626_add_environment_slug.rb b/db/migrate/20161207231626_add_environment_slug.rb
new file mode 100644
index 00000000000..7153e6a32b1
--- /dev/null
+++ b/db/migrate/20161207231626_add_environment_slug.rb
@@ -0,0 +1,60 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddEnvironmentSlug < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+ DOWNTIME_REASON = 'Adding NOT NULL column environments.slug with dependent data'
+
+ # Used to generate random suffixes for the slug
+ NUMBERS = '0'..'9'
+ SUFFIX_CHARS = ('a'..'z').to_a + NUMBERS.to_a
+
+ def up
+ environments = Arel::Table.new(:environments)
+
+ add_column :environments, :slug, :string
+ finder = environments.project(:id, :name)
+
+ connection.exec_query(finder.to_sql).rows.each do |id, name|
+ updater = Arel::UpdateManager.new(ActiveRecord::Base).
+ table(environments).
+ set(environments[:slug] => generate_slug(name)).
+ where(environments[:id].eq(id))
+
+ connection.exec_update(updater.to_sql, self.class.name, [])
+ end
+
+ change_column_null :environments, :slug, false
+ end
+
+ def down
+ remove_column :environments, :slug
+ end
+
+ # Copy of the Environment#generate_slug implementation
+ def generate_slug(name)
+ # Lowercase letters and numbers only
+ slugified = name.to_s.downcase.gsub(/[^a-z0-9]/, '-')
+
+ # Must start with a letter
+ slugified = "env-" + slugified if NUMBERS.cover?(slugified[0])
+
+ # Maximum length: 24 characters (OpenShift limitation)
+ slugified = slugified[0..23]
+
+ # Cannot end with a "-" character (Kubernetes label limitation)
+ slugified = slugified[0..-2] if slugified[-1] == "-"
+
+ # Add a random suffix, shortening the current string if necessary, if it
+ # has been slugified. This ensures uniqueness.
+ slugified = slugified[0..16] + "-" + random_suffix if slugified != name
+
+ slugified
+ end
+
+ def random_suffix
+ (0..5).map { SUFFIX_CHARS.sample }.join
+ end
+end
diff --git a/db/migrate/20161209153400_add_unique_index_for_environment_slug.rb b/db/migrate/20161209153400_add_unique_index_for_environment_slug.rb
new file mode 100644
index 00000000000..e9fcef1cd45
--- /dev/null
+++ b/db/migrate/20161209153400_add_unique_index_for_environment_slug.rb
@@ -0,0 +1,15 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddUniqueIndexForEnvironmentSlug < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+ DOWNTIME_REASON = 'Adding a *unique* index to environments.slug'
+
+ disable_ddl_transaction!
+
+ def change
+ add_concurrent_index :environments, [:project_id, :slug], unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4711b7873af..67ff83d96d9 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -98,14 +98,14 @@ ActiveRecord::Schema.define(version: 20161212142807) do
t.text "help_page_text_html"
t.text "shared_runners_text_html"
t.text "after_sign_up_text_html"
+ t.boolean "sidekiq_throttling_enabled", default: false
+ t.string "sidekiq_throttling_queues"
+ t.decimal "sidekiq_throttling_factor"
t.boolean "housekeeping_enabled", default: true, null: false
t.boolean "housekeeping_bitmaps_enabled", default: true, null: false
t.integer "housekeeping_incremental_repack_period", default: 10, null: false
t.integer "housekeeping_full_repack_period", default: 50, null: false
t.integer "housekeeping_gc_period", default: 200, null: false
- t.boolean "sidekiq_throttling_enabled", default: false
- t.string "sidekiq_throttling_queues"
- t.decimal "sidekiq_throttling_factor"
t.boolean "html_emails_enabled", default: true
end
@@ -428,9 +428,11 @@ ActiveRecord::Schema.define(version: 20161212142807) do
t.string "external_url"
t.string "environment_type"
t.string "state", default: "available", null: false
+ t.string "slug", null: false
end
- add_index "environments", ["project_id", "name"], name: "index_environments_on_project_id_and_name", using: :btree
+ add_index "environments", ["project_id", "name"], name: "index_environments_on_project_id_and_name", unique: true, using: :btree
+ add_index "environments", ["project_id", "slug"], name: "index_environments_on_project_id_and_slug", unique: true, using: :btree
create_table "events", force: :cascade do |t|
t.string "target_type"
@@ -737,8 +739,8 @@ ActiveRecord::Schema.define(version: 20161212142807) do
t.integer "visibility_level", default: 20, null: false
t.boolean "request_access_enabled", default: false, null: false
t.datetime "deleted_at"
- t.text "description_html"
t.boolean "lfs_enabled"
+ t.text "description_html"
t.integer "parent_id"
end
@@ -1219,8 +1221,8 @@ ActiveRecord::Schema.define(version: 20161212142807) do
t.datetime "otp_grace_period_started_at"
t.boolean "ldap_email", default: false, null: false
t.boolean "external", default: false
- t.string "incoming_email_token"
t.string "organization"
+ t.string "incoming_email_token"
t.boolean "authorized_projects_populated"
end
@@ -1290,4 +1292,4 @@ ActiveRecord::Schema.define(version: 20161212142807) do
add_foreign_key "subscriptions", "projects", on_delete: :cascade
add_foreign_key "trending_projects", "projects", on_delete: :cascade
add_foreign_key "u2f_registrations", "users"
-end \ No newline at end of file
+end
diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md
index b4a953d1ccc..76029b30dd8 100644
--- a/doc/administration/environment_variables.md
+++ b/doc/administration/environment_variables.md
@@ -13,17 +13,18 @@ override certain values.
Variable | Type | Description
-------- | ---- | -----------
-`GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation
-`GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`)
-`RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging` or `test`
-`DATABASE_URL` | string | The database URL; is of the form: `postgresql://localhost/blog_development`
-`GITLAB_EMAIL_FROM` | string | The e-mail address used in the "From" field in e-mails sent by GitLab
-`GITLAB_EMAIL_DISPLAY_NAME` | string | The name used in the "From" field in e-mails sent by GitLab
-`GITLAB_EMAIL_REPLY_TO` | string | The e-mail address used in the "Reply-To" field in e-mails sent by GitLab
-`GITLAB_EMAIL_REPLY_TO` | string | The e-mail address used in the "Reply-To" field in e-mails sent by GitLab
-`GITLAB_EMAIL_SUBJECT_SUFFIX` | string | The e-mail subject suffix used in e-mails sent by GitLab
-`GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the Unicorn worker killer
-`GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the Unicorn worker killer
+`GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation
+`GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`)
+`RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging` or `test`
+`DATABASE_URL` | string | The database URL; is of the form: `postgresql://localhost/blog_development`
+`GITLAB_EMAIL_FROM` | string | The e-mail address used in the "From" field in e-mails sent by GitLab
+`GITLAB_EMAIL_DISPLAY_NAME` | string | The name used in the "From" field in e-mails sent by GitLab
+`GITLAB_EMAIL_REPLY_TO` | string | The e-mail address used in the "Reply-To" field in e-mails sent by GitLab
+`GITLAB_EMAIL_REPLY_TO` | string | The e-mail address used in the "Reply-To" field in e-mails sent by GitLab
+`GITLAB_EMAIL_SUBJECT_SUFFIX` | string | The e-mail subject suffix used in e-mails sent by GitLab
+`GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the Unicorn worker killer
+`GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the Unicorn worker killer
+`GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN` | string | Sets the initial registration token used for GitLab Runners
## Complete database variables
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index d757a3c2a66..4b8d5c5cc87 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -1,4 +1,4 @@
-## Log system
+# Log system
GitLab has an advanced log system where everything is logged so that you
can analyze your instance using various system log files. In addition to
@@ -9,10 +9,10 @@ documentation](http://docs.gitlab.com/ee/administration/audit_events.html)
System log files are typically plain text in a standard log file format.
This guide talks about how to read and use these system log files.
-### production.log
+## `production.log`
This file lives in `/var/log/gitlab/gitlab-rails/production.log` for
-omnibus package or in `/home/git/gitlab/log/production.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/production.log` for
installations from source. (When Gitlab is running in an environment
other than production, the corresponding logfile is shown here.)
@@ -46,10 +46,10 @@ In this example we can see that server processed an HTTP request with URL
19:34:53 +0200. Also we can see that request was processed by
`Projects::TreeController`.
-### application.log
+## `application.log`
This file lives in `/var/log/gitlab/gitlab-rails/application.log` for
-omnibus package or in `/home/git/gitlab/log/application.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/application.log` for
installations from source.
It helps you discover events happening in your instance such as user creation,
@@ -63,10 +63,10 @@ October 07, 2014 11:25: User "Claudie Hodkiewicz" (nasir_stehr@olson.co.uk) was
October 07, 2014 11:25: Project "project133" was removed
```
-### githost.log
+## `githost.log`
This file lives in `/var/log/gitlab/gitlab-rails/githost.log` for
-omnibus package or in `/home/git/gitlab/log/githost.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/githost.log` for
installations from source.
GitLab has to interact with Git repositories but in some rare cases
@@ -81,10 +81,10 @@ December 03, 2014 13:20 -> ERROR -> Command failed [1]: /usr/bin/git --git-dir=/
error: failed to push some refs to '/Users/vsizov/gitlab-development-kit/repositories/gitlabhq/gitlab_git.git'
```
-### sidekiq.log
+## `sidekiq.log`
This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for
-omnibus package or in `/home/git/gitlab/log/sidekiq.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/sidekiq.log` for
installations from source.
GitLab uses background jobs for processing tasks which can take a long
@@ -96,10 +96,10 @@ this file. For example:
2014-06-10T18:18:26Z 14299 TID-55uqo INFO: Booting Sidekiq 3.0.0 with redis options {:url=>"redis://localhost:6379/0", :namespace=>"sidekiq"}
```
-### gitlab-shell.log
+## `gitlab-shell.log`
This file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log` for
-omnibus package or in `/home/git/gitlab-shell/gitlab-shell.log` for
+Omnibus GitLab packages or in `/home/git/gitlab-shell/gitlab-shell.log` for
installations from source.
GitLab shell is used by Gitlab for executing Git commands and provide
@@ -110,10 +110,10 @@ I, [2015-02-13T06:17:00.671315 #9291] INFO -- : Adding project root/example.git
I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory and symlinking global hooks directory for /var/opt/gitlab/git-data/repositories/root/example.git.
```
-### unicorn\_stderr.log
+## `unicorn\_stderr.log`
This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for
-omnibus package or in `/home/git/gitlab/log/unicorn_stderr.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/unicorn_stderr.log` for
installations from source.
Unicorn is a high-performance forking Web server which is used for
@@ -136,3 +136,13 @@ I, [2015-02-13T07:16:01.530733 #9047] INFO -- : reaped #<Process::Status: pid 9
I, [2015-02-13T07:16:01.534501 #13379] INFO -- : worker=1 spawned pid=13379
I, [2015-02-13T07:16:01.534848 #13379] INFO -- : worker=1 ready
```
+
+## `repocheck.log`
+
+This file lives in `/var/log/gitlab/gitlab-rails/repocheck.log` for
+Omnibus GitLab packages or in `/home/git/gitlab/log/repocheck.log` for
+installations from source.
+
+It logs information whenever a [repository check is run][repocheck] on a project.
+
+[repocheck]: repository_checks.md
diff --git a/doc/api/enviroments.md b/doc/api/enviroments.md
index 87a5fa67124..1299aca8c45 100644
--- a/doc/api/enviroments.md
+++ b/doc/api/enviroments.md
@@ -22,8 +22,9 @@ Example response:
[
{
"id": 1,
- "name": "Env1",
- "external_url": "https://env1.example.gitlab.com"
+ "name": "review/fix-foo",
+ "slug": "review-fix-foo-dfjre3",
+ "external_url": "https://review-fix-foo-dfjre3.example.gitlab.com"
}
]
```
@@ -54,6 +55,7 @@ Example response:
{
"id": 1,
"name": "deploy",
+ "slug": "deploy",
"external_url": "https://deploy.example.gitlab.com"
}
```
@@ -85,6 +87,7 @@ Example response:
{
"id": 1,
"name": "staging",
+ "slug": "staging",
"external_url": "https://staging.example.gitlab.com"
}
```
@@ -112,6 +115,7 @@ Example response:
{
"id": 1,
"name": "deploy",
+ "slug": "deploy",
"external_url": "https://deploy.example.gitlab.com"
}
```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 0bc2a5210aa..edffad555a5 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -664,7 +664,6 @@ Parameters:
| `path` | string | no | Custom repository name for new project. By default generated based on name |
| `default_branch` | string | no | `master` by default |
| `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
-| `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index 9dd84a5ff81..bad0233a9ce 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -86,6 +86,13 @@ will later see, is exposed in various places within GitLab. Each time a job that
has an environment specified and succeeds, a deployment is recorded, remembering
the Git SHA and environment name.
+>**Note:**
+Starting with GitLab 8.15, the environment name is exposed to the Runner in
+two forms: `$CI_ENVIRONMENT_NAME`, and `$CI_ENVIRONMENT_SLUG`. The first is
+the name given in `.gitlab-ci.yml` (with any variables expanded), while the
+second is a "cleaned-up" version of the name, suitable for use in URLs, DNS,
+etc.
+
To sum up, with the above `.gitlab-ci.yml` we have achieved that:
- All branches will run the `test` and `build` jobs.
@@ -157,7 +164,7 @@ that can be found in the deployments page
job with the commit associated with it.
>**Note:**
-Bare in mind that your mileage will vary and it's entirely up to how you define
+Bear in mind that your mileage will vary and it's entirely up to how you define
the deployment process in the job's `script` whether the rollback succeeds or not.
GitLab CI is just following orders.
@@ -248,7 +255,7 @@ deploy_review:
- echo "Deploy a review app"
environment:
name: review/$CI_BUILD_REF_NAME
- url: https://$CI_BUILD_REF_NAME.example.com
+ url: https://$CI_BUILD_REF_SLUG.review.example.com
only:
- branches
except:
@@ -259,15 +266,25 @@ Let's break it down in pieces. The job's name is `deploy_review` and it runs
on the `deploy` stage. The `script` at this point is fictional, you'd have to
use your own based on your deployment. Then, we set the `environment` with the
`environment:name` being `review/$CI_BUILD_REF_NAME`. Now that's an interesting
-one. Since the [environment name][env-name] can contain also slashes (`/`), we
-can use this pattern to distinguish between dynamic environments and the regular
+one. Since the [environment name][env-name] can contain slashes (`/`), we can
+use this pattern to distinguish between dynamic environments and the regular
ones.
So, the first part is `review`, followed by a `/` and then `$CI_BUILD_REF_NAME`
-which takes the value of the branch name. We also use the same
-`$CI_BUILD_REF_NAME` value in the `environment:url` so that the environment
-can get a specific and distinct URL for each branch. Again, the way you set up
-the webserver to serve these requests is based on your setup.
+which takes the value of the branch name. Since `$CI_BUILD_REF_NAME` itself may
+also contain `/`, or other characters that would be invalid in a domain name or
+URL, we use `$CI_ENVIRONMENT_SLUG` in the `environment:url` so that the
+environment can get a specific and distinct URL for each branch. In this case,
+given a `$CI_BUILD_REF_NAME` of `100-Do-The-Thing`, the URL will be something
+like `https://review-100-do-the-4f99a2.example.com`. Again, the way you set up
+the web server to serve these requests is based on your setup.
+
+You could also use `$CI_BUILD_REF_SLUG` in `environment:url`, e.g.:
+`https://$CI_BUILD_REF_SLUG.review.example.com`. We use `$CI_ENVIRONMENT_SLUG`
+here because it is guaranteed to be unique, but if you're using a workflow like
+[GitLab Flow][gitlab-flow], collisions are very unlikely, and you may prefer
+environment names to be more closely based on the branch name - the example
+above would give you an URL like `https://100-do-the-thing.review.example.com`
Last but not least, we tell the job to run [`only`][only] on branches
[`except`][only] master.
@@ -299,7 +316,7 @@ deploy_review:
- echo "Deploy a review app"
environment:
name: review/$CI_BUILD_REF_NAME
- url: https://$CI_BUILD_REF_NAME.example.com
+ url: https://$CI_ENVIRONMENT_SLUG.example.com
only:
- branches
except:
@@ -329,16 +346,16 @@ deploy_prod:
A more realistic example would include copying files to a location where a
webserver (NGINX) could then read and serve. The example below will copy the
-`public` directory to `/srv/nginx/$CI_BUILD_REF_NAME/public`:
+`public` directory to `/srv/nginx/$CI_BUILD_REF_SLUG/public`:
```yaml
review_app:
stage: deploy
script:
- - rsync -av --delete public /srv/nginx/$CI_BUILD_REF_NAME
+ - rsync -av --delete public /srv/nginx/$CI_BUILD_REF_SLUG
environment:
name: review/$CI_BUILD_REF_NAME
- url: https://$CI_BUILD_REF_NAME.example.com
+ url: https://$CI_BUILD_REF_SLUG.example.com
```
It is assumed that the user has already setup NGINX and GitLab Runner in the
@@ -346,7 +363,7 @@ server this job will run on.
>**Note:**
Be sure to check out the [limitations](#limitations) section for some edge
-cases regarding naming of you branches and Review Apps.
+cases regarding naming of your branches and Review Apps.
---
@@ -418,7 +435,7 @@ deploy_review:
- echo "Deploy a review app"
environment:
name: review/$CI_BUILD_REF_NAME
- url: https://$CI_BUILD_REF_NAME.example.com
+ url: https://$CI_ENVIRONMENT_SLUG.example.com
on_stop: stop_review
only:
- branches
@@ -480,9 +497,8 @@ exist, you should see something like:
## Checkout deployments locally
-Since 8.13, a reference in the git repository is saved for each deployment. So
-knowing what the state is of your current environments is only a `git fetch`
-away.
+Since 8.13, a reference in the git repository is saved for each deployment, so
+knowing the state of your current environments is only a `git fetch` away.
In your git config, append the `[remote "<your-remote>"]` block with an extra
fetch line:
@@ -493,10 +509,6 @@ fetch = +refs/environments/*:refs/remotes/origin/environments/*
## Limitations
-1. If the branch name contains special characters (`/`), and you use the
- `$CI_BUILD_REF_NAME` variable to dynamically create environments, there might
- be complications during your Review Apps deployment. Follow the
- [issue 22849][ce-22849] for more information.
1. You are limited to use only the [CI predefined variables][variables] in the
`environment: name`. If you try to re-use variables defined inside `script`
as part of the environment name, it will not work.
@@ -520,6 +532,6 @@ Below are some links you may find interesting:
[only]: yaml/README.md#only-and-except
[onstop]: yaml/README.md#environment-on_stop
[ce-7015]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7015
-[ce-22849]: https://gitlab.com/gitlab-org/gitlab-ce/issues/22849
+[gitlab-flow]: ../workflow/gitlab_flow.md
[gitlab runner]: https://docs.gitlab.com/runner/
[git-strategy]: yaml/README.md#git-strategy
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index d142fe266a2..eb540a50606 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -40,6 +40,7 @@ version of Runner required.
| **CI_BUILD_NAME** | all | 0.5 | The name of the build as defined in `.gitlab-ci.yml` |
| **CI_BUILD_STAGE** | all | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
| **CI_BUILD_REF_NAME** | all | all | The branch or tag name for which project is built |
+| **CI_BUILD_REF_SLUG** | 8.15 | all | `$CI_BUILD_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
| **CI_BUILD_REPO** | all | all | The URL to clone the Git repository |
| **CI_BUILD_TRIGGERED** | all | 0.5 | The flag to indicate that build was [triggered] |
| **CI_BUILD_MANUAL** | 8.12 | all | The flag to indicate that build was manually started |
@@ -51,6 +52,8 @@ version of Runner required.
| **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name |
| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project |
| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the build is run |
+| **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this build |
+| **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. |
| **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry |
| **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project |
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 5f88974d360..a62193700fc 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -690,18 +690,12 @@ The `stop_review_app` job is **required** to have the following keywords defined
#### dynamic environments
> [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6.
+ `$CI_ENVIRONMENT_SLUG` was [introduced][ce-7983] in GitLab 8.15
`environment` can also represent a configuration hash with `name` and `url`.
These parameters can use any of the defined [CI variables](#variables)
(including predefined, secure variables and `.gitlab-ci.yml` variables).
->**Note:**
-Be aware than if the branch name contains special characters and you use the
-`$CI_BUILD_REF_NAME` variable to dynamically create environments, there might
-be complications during deployment. Follow the
-[issue 22849](https://gitlab.com/gitlab-org/gitlab-ce/issues/22849) for more
-information.
-
For example:
```
@@ -709,15 +703,17 @@ deploy as review app:
stage: deploy
script: make deploy
environment:
- name: review-apps/$CI_BUILD_REF_NAME
- url: https://$CI_BUILD_REF_NAME.review.example.com/
+ name: review/$CI_BUILD_REF_NAME
+ url: https://$CI_ENVIRONMENT_SLUG.example.com/
```
The `deploy as review app` job will be marked as deployment to dynamically
-create the `review-apps/$CI_BUILD_REF_NAME` environment, which `$CI_BUILD_REF_NAME`
-is an [environment variable][variables] set by the Runner. If for example the
-`deploy as review app` job was run in a branch named `pow`, this environment
-should be accessible under `https://pow.review.example.com/`.
+create the `review/$CI_BUILD_REF_NAME` environment, where `$CI_BUILD_REF_NAME`
+is an [environment variable][variables] set by the Runner. The
+`$CI_ENVIRONMENT_SLUG` variable is based on the environment name, but suitable
+for inclusion in URLs. In this case, if the `deploy as review app` job was run
+in a branch named `pow`, this environment would be accessible with an URL like
+`https://review-pow-aaaaaa.example.com/`.
This of course implies that the underlying server which hosts the application
is properly configured.
@@ -1246,3 +1242,4 @@ CI with various languages.
[environment]: ../environments.md
[ce-6669]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6669
[variables]: ../variables/README.md
+[ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983
diff --git a/doc/project_services/img/kubernetes_configuration.png b/doc/project_services/img/kubernetes_configuration.png
new file mode 100644
index 00000000000..349a2dc8456
--- /dev/null
+++ b/doc/project_services/img/kubernetes_configuration.png
Binary files differ
diff --git a/doc/project_services/kubernetes.md b/doc/project_services/kubernetes.md
new file mode 100644
index 00000000000..cb577b608b4
--- /dev/null
+++ b/doc/project_services/kubernetes.md
@@ -0,0 +1,38 @@
+# GitLab Kubernetes / OpenShift integration
+
+GitLab can be configured to interact with Kubernetes, or other systems using the
+Kubernetes API (such as OpenShift).
+
+Each project can be configured to connect to a different Kubernetes cluster, see
+the [configuration](#configuration) section.
+
+If you have a single cluster that you want to use for all your projects,
+you can pre-fill the settings page with a default template. To configure the
+template, see the [Services Templates](services-templates.md) document.
+
+## Configuration
+
+![Kubernetes configuration settings](img/kubernetes_configuration.png)
+
+The Kubernetes service takes the following arguments:
+
+1. Kubernetes namespace
+1. API URL
+1. Service token
+1. Custom CA bundle
+
+The API URL is the URL that GitLab uses to access the Kubernetes API. Kubernetes
+exposes several APIs - we want the "base" URL that is common to all of them,
+e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`.
+
+GitLab authenticates against Kubernetes using service tokens, which are
+scoped to a particular `namespace`. If you don't have a service token yet,
+you can follow the
+[Kubernetes documentation](http://kubernetes.io/docs/user-guide/service-accounts/)
+to create one. You can also view or create service tokens in the
+[Kubernetes dashboard](http://kubernetes.io/docs/user-guide/ui/) - visit
+`Config -> Secrets`.
+
+Fill in the service token and namespace according to the values you just got.
+If the API is using a self-signed TLS certificate, you'll also need to include
+the `ca.crt` contents as the `Custom CA bundle`.
diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md
index 890f7525b0e..a7bcd186a8c 100644
--- a/doc/project_services/project_services.md
+++ b/doc/project_services/project_services.md
@@ -42,6 +42,7 @@ further configuration instructions and details. Contributions are welcome.
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
| [JIRA](jira.md) | JIRA issue tracker |
| JetBrains TeamCity CI | A continuous integration and build server |
+| [Kubernetes](kubernetes.md) | A containerized deployment service |
| [Mattermost slash commands](mattermost_slash_commands.md) | Mattermost chat and ChatOps slash commands |
| PivotalTracker | Project Management Software (Source Commits Endpoint) |
| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop |
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 4d24eb21976..f6484688721 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -319,6 +319,40 @@ Here's a sample video:
![Sample Video](img/markdown_video.mp4)
+### Math
+
+> If this is not rendered correctly, see
+https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#math
+
+It is possible to have math written with the LaTeX syntax rendered using [KaTeX][katex].
+
+Math written inside ```$``$``` will be rendered inline with the text.
+
+Math written inside triple back quotes, with the language declared as `math`, will be rendered on a separate line.
+
+Example:
+
+ This math is inline $`a^2+b^2=c^2`$.
+
+ This is on a separate line
+ ```math
+ a^2+b^2=c^2
+ ```
+
+Becomes:
+
+This math is inline $`a^2+b^2=c^2`$.
+
+This is on a separate line
+```math
+a^2+b^2=c^2
+```
+
+_Be advised that KaTeX only supports a [subset][katex-subset] of LaTeX._
+
+>**Note:**
+This also works for the asciidoctor `:stem: latexmath`. For details see the [asciidoctor user manual][asciidoctor-manual].
+
## Standard Markdown
### Headers
@@ -764,3 +798,6 @@ A link starting with a `/` is relative to the wiki root.
[markdown.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md
[rouge]: http://rouge.jneen.net/ "Rouge website"
[redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website"
+[katex]: https://github.com/Khan/KaTeX "KaTeX website"
+[katex-subset]: https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX "Macros supported by KaTeX"
+[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual" \ No newline at end of file
diff --git a/features/admin/active_tab.feature b/features/admin/active_tab.feature
deleted file mode 100644
index f5bb06dea7d..00000000000
--- a/features/admin/active_tab.feature
+++ /dev/null
@@ -1,54 +0,0 @@
-@admin
-Feature: Admin Active Tab
- Background:
- Given I sign in as an admin
-
- Scenario: On Admin Home
- Given I visit admin page
- Then the active main tab should be Overview
- And no other main tabs should be active
-
- Scenario: On Admin Projects
- Given I visit admin projects page
- Then the active main tab should be Overview
- And the active sub tab should be Projects
- And no other main tabs should be active
- And no other sub tabs should be active
-
- Scenario: On Admin Groups
- Given I visit admin groups page
- Then the active main tab should be Overview
- And the active sub tab should be Groups
- And no other main tabs should be active
- And no other sub tabs should be active
-
- Scenario: On Admin Users
- Given I visit admin users page
- Then the active main tab should be Overview
- And the active sub tab should be Users
- And no other main tabs should be active
- And no other sub tabs should be active
-
- Scenario: On Admin Logs
- Given I visit admin logs page
- Then the active main tab should be Monitoring
- And the active sub tab should be Logs
- And no other main tabs should be active
- And no other sub tabs should be active
-
- Scenario: On Admin Messages
- Given I visit admin messages page
- Then the active main tab should be Messages
- And no other main tabs should be active
-
- Scenario: On Admin Hooks
- Given I visit admin hooks page
- Then the active main tab should be Hooks
- And no other main tabs should be active
-
- Scenario: On Admin Resque
- Given I visit admin Resque page
- Then the active main tab should be Monitoring
- And the active sub tab should be Resque
- And no other main tabs should be active
- And no other sub tabs should be active
diff --git a/features/steps/admin/active_tab.rb b/features/steps/admin/active_tab.rb
deleted file mode 100644
index 9b1689a8198..00000000000
--- a/features/steps/admin/active_tab.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-class Spinach::Features::AdminActiveTab < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedActiveTab
-
- step 'the active main tab should be Overview' do
- ensure_active_main_tab('Overview')
- end
-
- step 'the active sub tab should be Projects' do
- ensure_active_sub_tab('Projects')
- end
-
- step 'the active sub tab should be Groups' do
- ensure_active_sub_tab('Groups')
- end
-
- step 'the active sub tab should be Users' do
- ensure_active_sub_tab('Users')
- end
-
- step 'the active main tab should be Hooks' do
- ensure_active_main_tab('Hooks')
- end
-
- step 'the active main tab should be Monitoring' do
- ensure_active_main_tab('Monitoring')
- end
-
- step 'the active sub tab should be Resque' do
- ensure_active_sub_tab('Background Jobs')
- end
-
- step 'the active sub tab should be Logs' do
- ensure_active_sub_tab('Logs')
- end
-
- step 'the active main tab should be Messages' do
- ensure_active_main_tab('Messages')
- end
-end
diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb
index f5fddab357d..c1d1eca9116 100644
--- a/features/steps/group/milestones.rb
+++ b/features/steps/group/milestones.rb
@@ -131,5 +131,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
issue.labels << project.labels.find_by(title: 'bug')
issue.labels << project.labels.find_by(title: 'feature')
end
+
+ current_user.refresh_authorized_projects
end
end
diff --git a/features/steps/shared/authentication.rb b/features/steps/shared/authentication.rb
index 735e0ef6108..5c3e724746b 100644
--- a/features/steps/shared/authentication.rb
+++ b/features/steps/shared/authentication.rb
@@ -33,6 +33,6 @@ module SharedAuthentication
end
def current_user
- @user || User.first
+ @user || User.reorder(nil).first
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 01c0f5072ba..dfbb3ab86dd 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -629,7 +629,7 @@ module API
end
class EnvironmentBasic < Grape::Entity
- expose :id, :name, :external_url
+ expose :id, :name, :slug, :external_url
end
class Environment < EnvironmentBasic
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 80bbd9bb6e4..1a7e68f0528 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -1,6 +1,7 @@
module API
# Environments RESTfull API endpoints
class Environments < Grape::API
+ include ::API::Helpers::CustomValidators
include PaginationParams
before { authenticate! }
@@ -29,6 +30,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the environment to be created'
optional :external_url, type: String, desc: 'URL on which this deployment is viewable'
+ optional :slug, absence: { message: "is automatically generated and cannot be changed" }
end
post ':id/environments' do
authorize! :create_environment, user_project
@@ -50,6 +52,7 @@ module API
requires :environment_id, type: Integer, desc: 'The environment ID'
optional :name, type: String, desc: 'The new environment name'
optional :external_url, type: String, desc: 'The new URL on which this deployment is viewable'
+ optional :slug, absence: { message: "is automatically generated and cannot be changed" }
end
put ':id/environments/:environment_id' do
authorize! :update_environment, user_project
diff --git a/lib/api/helpers/custom_validators.rb b/lib/api/helpers/custom_validators.rb
new file mode 100644
index 00000000000..0a8f3073a50
--- /dev/null
+++ b/lib/api/helpers/custom_validators.rb
@@ -0,0 +1,14 @@
+module API
+ module Helpers
+ module CustomValidators
+ class Absence < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ return if params.respond_to?(:key?) && !params.key?(attr_name)
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:absence)
+ end
+ end
+ end
+ end
+end
+
+Grape::Validations.register_validator(:absence, ::API::Helpers::CustomValidators::Absence)
diff --git a/lib/api/services.rb b/lib/api/services.rb
index fde2e2746f1..b1e072b4f47 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -351,6 +351,34 @@ module API
desc: 'The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`'
}
],
+
+ 'kubernetes' => [
+ {
+ required: true,
+ name: :namespace,
+ type: String,
+ desc: 'The Kubernetes namespace to use'
+ },
+ {
+ required: true,
+ name: :api_url,
+ type: String,
+ desc: 'The URL to the Kubernetes cluster API, e.g., https://kubernetes.example.com'
+ },
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'The service token to authenticate against the Kubernetes cluster with'
+ },
+ {
+ required: false,
+ name: :ca_pem,
+ type: String,
+ desc: 'A custom certificate authority bundle to verify the Kubernetes cluster with (PEM format)'
+ },
+ ],
+
'mattermost-slash-commands' => [
{
required: true,
diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb
new file mode 100644
index 00000000000..cb037f89337
--- /dev/null
+++ b/lib/banzai/filter/math_filter.rb
@@ -0,0 +1,51 @@
+require 'uri'
+
+module Banzai
+ module Filter
+ # HTML filter that adds class="code math" and removes the dollar sign in $`2+2`$.
+ #
+ class MathFilter < HTML::Pipeline::Filter
+ # This picks out <code>...</code>.
+ INLINE_MATH = 'descendant-or-self::code'.freeze
+
+ # Pick out a code block which is declared math
+ DISPLAY_MATH = "descendant-or-self::pre[contains(@class, 'math') and contains(@class, 'code')]".freeze
+
+ # Attribute indicating inline or display math.
+ STYLE_ATTRIBUTE = 'data-math-style'.freeze
+
+ # Class used for tagging elements that should be rendered
+ TAG_CLASS = 'js-render-math'.freeze
+
+ INLINE_CLASSES = "code math #{TAG_CLASS}".freeze
+
+ DOLLAR_SIGN = '$'.freeze
+
+ def call
+ doc.xpath(INLINE_MATH).each do |code|
+ closing = code.next
+ opening = code.previous
+
+ # We need a sibling before and after.
+ # They should end and start with $ respectively.
+ if closing && opening &&
+ closing.content.first == DOLLAR_SIGN &&
+ opening.content.last == DOLLAR_SIGN
+
+ code[:class] = INLINE_CLASSES
+ code[STYLE_ATTRIBUTE] = 'inline'
+ closing.content = closing.content[1..-1]
+ opening.content = opening.content[0..-2]
+ end
+ end
+
+ doc.xpath(DISPLAY_MATH).each do |el|
+ el[STYLE_ATTRIBUTE] = 'display'
+ el[:class] += " #{TAG_CLASS}"
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 5da2d0b008c..5a1f873496c 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -6,6 +6,7 @@ module Banzai
Filter::SyntaxHighlightFilter,
Filter::SanitizationFilter,
+ Filter::MathFilter,
Filter::UploadLinkFilter,
Filter::VideoLinkFilter,
Filter::ImageLinkFilter,
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index 9667df4ffb8..f77f412da56 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -1,4 +1,5 @@
require 'asciidoctor'
+require 'asciidoctor/converter/html5'
module Gitlab
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
@@ -23,7 +24,7 @@ module Gitlab
def self.render(input, context, asciidoc_opts = {})
asciidoc_opts.reverse_merge!(
safe: :secure,
- backend: :html5,
+ backend: :gitlab_html5,
attributes: []
)
asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS)
@@ -36,3 +37,31 @@ module Gitlab
end
end
end
+
+module Gitlab
+ module Asciidoc
+ class Html5Converter < Asciidoctor::Converter::Html5Converter
+ extend Asciidoctor::Converter::Config
+
+ register_for 'gitlab_html5'
+
+ def stem(node)
+ return super unless node.style.to_sym == :latexmath
+
+ %(<pre#{id_attribute(node)} class="code math js-render-math #{node.role}" data-math-style="display"><code>#{node.content}</code></pre>)
+ end
+
+ def inline_quoted(node)
+ return super unless node.type.to_sym == :latexmath
+
+ %(<code#{id_attribute(node)} class="code math js-render-math #{node.role}" data-math-style="inline">#{node.text}</code>)
+ end
+
+ private
+
+ def id_attribute(node)
+ node.id ? %( id="#{node.id}") : nil
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/badge/build/status.rb b/lib/gitlab/badge/build/status.rb
index 50aa45e5406..b762d85b6e5 100644
--- a/lib/gitlab/badge/build/status.rb
+++ b/lib/gitlab/badge/build/status.rb
@@ -20,8 +20,8 @@ module Gitlab
def status
@project.pipelines
- .where(sha: @sha, ref: @ref)
- .status || 'unknown'
+ .where(sha: @sha)
+ .latest_status(@ref) || 'unknown'
end
def metadata
diff --git a/lib/gitlab/gfm/uploads_rewriter.rb b/lib/gitlab/gfm/uploads_rewriter.rb
index abc8c8c55e6..8fab5489616 100644
--- a/lib/gitlab/gfm/uploads_rewriter.rb
+++ b/lib/gitlab/gfm/uploads_rewriter.rb
@@ -1,3 +1,5 @@
+require 'fileutils'
+
module Gitlab
module Gfm
##
@@ -22,7 +24,9 @@ module Gitlab
return markdown unless file.try(:exists?)
new_uploader = FileUploader.new(target_project)
- new_uploader.store!(file)
+ with_link_in_tmp_dir(file.file) do |open_tmp_file|
+ new_uploader.store!(open_tmp_file)
+ end
new_uploader.to_markdown
end
end
@@ -46,6 +50,19 @@ module Gitlab
uploader.retrieve_from_store!(file)
uploader.file
end
+
+ # Because the uploaders use 'move_to_store' we must have a temporary
+ # file that is allowed to be (re)moved.
+ def with_link_in_tmp_dir(file)
+ dir = Dir.mktmpdir('UploadsRewriter', File.dirname(file))
+ # The filename matters to Carrierwave so we make sure to preserve it
+ tmp_file = File.join(dir, File.basename(file))
+ File.link(file, tmp_file)
+ # Open the file to placate Carrierwave
+ File.open(tmp_file) { |open_file| yield open_file }
+ ensure
+ FileUtils.rm_rf(dir)
+ end
end
end
end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 2c21804fe7a..4d4e04e9e35 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -8,6 +8,8 @@ module Gitlab
gon.shortcuts_path = help_page_path('shortcuts')
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
gon.award_menu_url = emojis_path
+ gon.katex_css_url = ActionController::Base.helpers.asset_path('katex.css')
+ gon.katex_js_url = ActionController::Base.helpers.asset_path('katex.js')
if current_user
gon.current_user_id = current_user.id
diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb
new file mode 100644
index 00000000000..65713e73a59
--- /dev/null
+++ b/lib/gitlab/middleware/multipart.rb
@@ -0,0 +1,99 @@
+# Gitlab::Middleware::Multipart - a Rack::Multipart replacement
+#
+# Rack::Multipart leaves behind tempfiles in /tmp and uses valuable Ruby
+# process time to copy files around. This alternative solution uses
+# gitlab-workhorse to clean up the tempfiles and puts the tempfiles in a
+# location where copying should not be needed.
+#
+# When gitlab-workhorse finds files in a multipart MIME body it sends
+# a signed message via a request header. This message lists the names of
+# the multipart entries that gitlab-workhorse filtered out of the
+# multipart structure and saved to tempfiles. Workhorse adds new entries
+# in the multipart structure with paths to the tempfiles.
+#
+# The job of this Rack middleware is to detect and decode the message
+# from workhorse. If present, it walks the Rack 'params' hash for the
+# current request, opens the respective tempfiles, and inserts the open
+# Ruby File objects in the params hash where Rack::Multipart would have
+# put them. The goal is that application code deeper down can keep
+# working the way it did with Rack::Multipart without changes.
+#
+# CAVEAT: the code that modifies the params hash is a bit complex. It is
+# conceivable that certain Rack params structures will not be modified
+# correctly. We are not aware of such bugs at this time though.
+#
+
+module Gitlab
+ module Middleware
+ class Multipart
+ RACK_ENV_KEY = 'HTTP_GITLAB_WORKHORSE_MULTIPART_FIELDS'
+
+ class Handler
+ def initialize(env, message)
+ @request = Rack::Request.new(env)
+ @rewritten_fields = message['rewritten_fields']
+ @open_files = []
+ end
+
+ def with_open_files
+ @rewritten_fields.each do |field, tmp_path|
+ parsed_field = Rack::Utils.parse_nested_query(field)
+ raise "unexpected field: #{field.inspect}" unless parsed_field.count == 1
+
+ key, value = parsed_field.first
+ if value.nil?
+ value = File.open(tmp_path)
+ @open_files << value
+ else
+ value = decorate_params_value(value, @request.params[key], tmp_path)
+ end
+ @request.update_param(key, value)
+ end
+
+ yield
+ ensure
+ @open_files.each(&:close)
+ end
+
+ # This function calls itself recursively
+ def decorate_params_value(path_hash, value_hash, tmp_path)
+ unless path_hash.is_a?(Hash) && path_hash.count == 1
+ raise "invalid path: #{path_hash.inspect}"
+ end
+ path_key, path_value = path_hash.first
+
+ unless value_hash.is_a?(Hash) && value_hash[path_key]
+ raise "invalid value hash: #{value_hash.inspect}"
+ end
+
+ case path_value
+ when nil
+ value_hash[path_key] = File.open(tmp_path)
+ @open_files << value_hash[path_key]
+ value_hash
+ when Hash
+ decorate_params_value(path_value, value_hash[path_key], tmp_path)
+ value_hash
+ else
+ raise "unexpected path value: #{path_value.inspect}"
+ end
+ end
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ encoded_message = env.delete(RACK_ENV_KEY)
+ return @app.call(env) if encoded_message.blank?
+
+ message = Gitlab::Workhorse.decode_jwt(encoded_message)[0]
+
+ Handler.new(env, message).with_open_files do
+ @app.call(env)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 66e6b29e798..6bdf3db9cb8 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -110,7 +110,7 @@ module Gitlab
end
def notes
- @notes ||= project.notes.user.search(query, as_user: @current_user).order('updated_at DESC')
+ @notes ||= NotesFinder.new(project, @current_user, search: query).execute.user.order('updated_at DESC')
end
def commits
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index d9d1e3cccca..9e0b0e5ea98 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -123,5 +123,22 @@ module Gitlab
def environment_name_regex_message
"can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.' and spaces"
end
+
+ def kubernetes_namespace_regex
+ /\A[a-z0-9]([-a-z0-9]*[a-z0-9])?\z/
+ end
+
+ def kubernetes_namespace_regex_message
+ "can contain only letters, digits or '-', and cannot start or end with '-'"
+ end
+
+ def environment_slug_regex
+ @environment_slug_regex ||= /\A[a-z]([a-z0-9-]*[a-z0-9])?\z/.freeze
+ end
+
+ def environment_slug_regex_message
+ "can contain only lowercase letters, digits, and '-'. " \
+ "Must start with a letter, and cannot end with '-'"
+ end
end
end
diff --git a/lib/gitlab/sql/union.rb b/lib/gitlab/sql/union.rb
index 1cd89b3a9c4..222021e8802 100644
--- a/lib/gitlab/sql/union.rb
+++ b/lib/gitlab/sql/union.rb
@@ -22,9 +22,7 @@ module Gitlab
# By using "unprepared_statements" we remove the usage of placeholders
# (thus fixing this problem), at a slight performance cost.
fragments = ActiveRecord::Base.connection.unprepared_statement do
- @relations.map do |rel|
- rel.reorder(nil).to_sql
- end
+ @relations.map { |rel| rel.reorder(nil).to_sql }.reject(&:blank?)
end
fragments.join("\nUNION\n")
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index 594439a5d4b..aeb1a26e1ba 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -117,8 +117,12 @@ module Gitlab
end
def verify_api_request!(request_headers)
+ decode_jwt(request_headers[INTERNAL_API_REQUEST_HEADER])
+ end
+
+ def decode_jwt(encoded_message)
JWT.decode(
- request_headers[INTERNAL_API_REQUEST_HEADER],
+ encoded_message,
secret,
true,
{ iss: 'gitlab-workhorse', verify_iss: true, algorithm: 'HS256' },
diff --git a/lib/rouge/lexers/math.rb b/lib/rouge/lexers/math.rb
new file mode 100644
index 00000000000..80784adfd76
--- /dev/null
+++ b/lib/rouge/lexers/math.rb
@@ -0,0 +1,21 @@
+module Rouge
+ module Lexers
+ class Math < Lexer
+ title "A passthrough lexer used for LaTeX input"
+ desc "A boring lexer that doesn't highlight anything"
+
+ tag 'math'
+ mimetypes 'text/plain'
+
+ default_options token: 'Text'
+
+ def token
+ @token ||= Token[option :token]
+ end
+
+ def stream_tokens(string, &b)
+ yield self.token, string
+ end
+ end
+ end
+end
diff --git a/public/404.html b/public/404.html
index 11b29d09a82..b3b3a0fa3f3 100644
--- a/public/404.html
+++ b/public/404.html
@@ -46,6 +46,14 @@
margin: 40px auto;
}
+ a {
+ line-height: 100px;
+ font-weight: normal;
+ color: #4A8BEE;
+ font-size: 18px;
+ text-decoration: none;
+ }
+
.container {
margin: auto 20px;
}
@@ -63,6 +71,7 @@
<hr />
<p>Make sure the address is correct and that the page hasn't moved.</p>
<p>Please contact your GitLab administrator if you think this is a mistake.</p>
+ <a href="javascript:history.back()">Go back</a>
</div>
</body>
</html>
diff --git a/public/422.html b/public/422.html
index 9bd7cb4b7c8..119e54ad8bd 100644
--- a/public/422.html
+++ b/public/422.html
@@ -46,6 +46,14 @@
margin: 40px auto;
}
+ a {
+ line-height: 100px;
+ font-weight: normal;
+ color: #4A8BEE;
+ font-size: 18px;
+ text-decoration: none;
+ }
+
.container {
margin: auto 20px;
}
@@ -63,6 +71,7 @@
<hr />
<p>Make sure you have access to the thing you tried to change.</p>
<p>Please contact your GitLab administrator if you think this is a mistake.</p>
+ <a href="javascript:history.back()">Go back</a>
</div>
</body>
</html>
diff --git a/public/500.html b/public/500.html
index f92e8839f8d..226ef3c40ea 100644
--- a/public/500.html
+++ b/public/500.html
@@ -46,6 +46,14 @@
margin: 40px auto;
}
+ a {
+ line-height: 100px;
+ font-weight: normal;
+ color: #4A8BEE;
+ font-size: 18px;
+ text-decoration: none;
+ }
+
.container {
margin: auto 20px;
}
@@ -63,6 +71,7 @@
<hr />
<p>Try refreshing the page, or going back and attempting the action again.</p>
<p>Please contact your GitLab administrator if this problem persists.</p>
+ <a href="javascript:history.back()">Go back</a>
</div>
</body>
</html>
diff --git a/public/502.html b/public/502.html
index c2be4f130a9..f037b81bace 100644
--- a/public/502.html
+++ b/public/502.html
@@ -46,6 +46,14 @@
margin: 40px auto;
}
+ a {
+ line-height: 100px;
+ font-weight: normal;
+ color: #4A8BEE;
+ font-size: 18px;
+ text-decoration: none;
+ }
+
.container {
margin: auto 20px;
}
@@ -63,6 +71,7 @@
<hr />
<p>Try refreshing the page, or going back and attempting the action again.</p>
<p>Please contact your GitLab administrator if this problem persists.</p>
+ <a href="javascript:history.back()">Go back</a>
</div>
</body>
</html>
diff --git a/public/503.html b/public/503.html
index 8850ffce362..f946a087871 100644
--- a/public/503.html
+++ b/public/503.html
@@ -46,6 +46,14 @@
margin: 40px auto;
}
+ a {
+ line-height: 100px;
+ font-weight: normal;
+ color: #4A8BEE;
+ font-size: 18px;
+ text-decoration: none;
+ }
+
.container {
margin: auto 20px;
}
@@ -63,6 +71,7 @@
<hr />
<p>Try refreshing the page, or going back and attempting the action again.</p>
<p>Please contact your GitLab administrator if this problem persists.</p>
+ <a href="javascript:history.back()">Go back</a>
</div>
</body>
</html>
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
new file mode 100644
index 00000000000..b7bb9290712
--- /dev/null
+++ b/spec/controllers/search_controller_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+describe SearchController do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, :public) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'finds issue comments' do
+ project = create(:empty_project, :public)
+ note = create(:note_on_issue, project: project)
+
+ get :show, project_id: project.id, scope: 'notes', search: note.note
+
+ expect(assigns[:search_objects].first).to eq note
+ end
+
+ context 'on restricted projects' do
+ context 'when signed out' do
+ before { sign_out(user) }
+
+ it "doesn't expose comments on issues" do
+ project = create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE)
+ note = create(:note_on_issue, project: project)
+
+ get :show, project_id: project.id, scope: 'notes', search: note.note
+
+ expect(assigns[:search_objects].count).to eq(0)
+ end
+ end
+
+ it "doesn't expose comments on issues" do
+ project = create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE)
+ note = create(:note_on_issue, project: project)
+
+ get :show, project_id: project.id, scope: 'notes', search: note.note
+
+ expect(assigns[:search_objects].count).to eq(0)
+ end
+
+ it "doesn't expose comments on merge_requests" do
+ project = create(:empty_project, :public, merge_requests_access_level: ProjectFeature::PRIVATE)
+ note = create(:note_on_merge_request, project: project)
+
+ get :show, project_id: project.id, scope: 'notes', search: note.note
+
+ expect(assigns[:search_objects].count).to eq(0)
+ end
+
+ it "doesn't expose comments on snippets" do
+ project = create(:empty_project, :public, snippets_access_level: ProjectFeature::PRIVATE)
+ note = create(:note_on_project_snippet, project: project)
+
+ get :show, project_id: project.id, scope: 'notes', search: note.note
+
+ expect(assigns[:search_objects].count).to eq(0)
+ end
+ end
+end
diff --git a/spec/db/production/settings.rb b/spec/db/production/settings.rb
new file mode 100644
index 00000000000..a7c5283df94
--- /dev/null
+++ b/spec/db/production/settings.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+require 'rainbow/ext/string'
+
+describe 'seed production settings', lib: true do
+ context 'GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN is set in the environment' do
+ before do
+ allow(ENV).to receive(:[]).and_call_original
+ allow(ENV).to receive(:[]).with('GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN').and_return('013456789')
+ end
+
+ it 'writes the token to the database' do
+ load(File.join(__dir__, '../../../db/fixtures/production/010_settings.rb'))
+ expect(ApplicationSetting.current.runners_registration_token).to eq('013456789')
+ end
+ end
+end
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 6919002dedc..a10ba629760 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -67,7 +67,7 @@ FactoryGirl.define do
end
trait :on_project_snippet do
- noteable { create(:snippet, project: project) }
+ noteable { create(:project_snippet, project: project) }
end
trait :system do
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 1166498ddff..0d072d6a690 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -133,4 +133,17 @@ FactoryGirl.define do
)
end
end
+
+ factory :kubernetes_project, parent: :empty_project do
+ after :create do |project|
+ project.create_kubernetes_service(
+ active: true,
+ properties: {
+ namespace: project.path,
+ api_url: 'https://kubernetes.example.com/api',
+ token: 'a' * 40,
+ }
+ )
+ end
+ end
end
diff --git a/spec/features/admin/admin_active_tab_spec.rb b/spec/features/admin/admin_active_tab_spec.rb
new file mode 100644
index 00000000000..f2eecc5b552
--- /dev/null
+++ b/spec/features/admin/admin_active_tab_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+RSpec.describe 'admin active tab' do
+ before do
+ login_as :admin
+ end
+
+ shared_examples 'page has active tab' do |title|
+ it "activates #{title} tab" do
+ expect(page).to have_selector('.layout-nav .nav-links > li.active', count: 1)
+ expect(page.find('.layout-nav li.active')).to have_content(title)
+ end
+ end
+
+ shared_examples 'page has active sub tab' do |title|
+ it "activates #{title} sub tab" do
+ expect(page).to have_selector('.sub-nav li.active', count: 1)
+ expect(page.find('.sub-nav li.active')).to have_content(title)
+ end
+ end
+
+ context 'on home page' do
+ before do
+ visit admin_root_path
+ end
+
+ it_behaves_like 'page has active tab', 'Overview'
+ end
+
+ context 'on projects' do
+ before do
+ visit admin_namespaces_projects_path
+ end
+
+ it_behaves_like 'page has active tab', 'Overview'
+ it_behaves_like 'page has active sub tab', 'Projects'
+ end
+
+ context 'on groups' do
+ before do
+ visit admin_groups_path
+ end
+
+ it_behaves_like 'page has active tab', 'Overview'
+ it_behaves_like 'page has active sub tab', 'Groups'
+ end
+
+ context 'on users' do
+ before do
+ visit admin_users_path
+ end
+
+ it_behaves_like 'page has active tab', 'Overview'
+ it_behaves_like 'page has active sub tab', 'Users'
+ end
+
+ context 'on logs' do
+ before do
+ visit admin_logs_path
+ end
+
+ it_behaves_like 'page has active tab', 'Monitoring'
+ it_behaves_like 'page has active sub tab', 'Logs'
+ end
+
+ context 'on messages' do
+ before do
+ visit admin_broadcast_messages_path
+ end
+
+ it_behaves_like 'page has active tab', 'Messages'
+ end
+
+ context 'on hooks' do
+ before do
+ visit admin_hooks_path
+ end
+
+ it_behaves_like 'page has active tab', 'Hooks'
+ end
+
+ context 'on background jobs' do
+ before do
+ visit admin_background_jobs_path
+ end
+
+ it_behaves_like 'page has active tab', 'Monitoring'
+ it_behaves_like 'page has active sub tab', 'Background Jobs'
+ end
+end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index cd0512a37e6..da64827b377 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -89,4 +89,12 @@ feature 'GFM autocomplete', feature: true, js: true do
end
end
end
+
+ it 'doesnt open autocomplete after non-word character' do
+ page.within '.timeline-content-form' do
+ find('#note_note').native.send_keys("@#{user.username[0..2]}!")
+ end
+
+ expect(page).not_to have_selector('.atwho-view')
+ end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 7f69e888f32..97737d7ddc7 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -10,24 +10,24 @@ describe IssuesFinder do
let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone, title: 'gitlab') }
let(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'gitlab') }
let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2) }
- let(:closed_issue) { create(:issue, author: user2, assignee: user2, project: project2, state: 'closed') }
- let!(:label_link) { create(:label_link, label: label, target: issue2) }
-
- before do
- project1.team << [user, :master]
- project2.team << [user, :developer]
- project2.team << [user2, :developer]
-
- issue1
- issue2
- issue3
- end
describe '#execute' do
+ let(:closed_issue) { create(:issue, author: user2, assignee: user2, project: project2, state: 'closed') }
+ let!(:label_link) { create(:label_link, label: label, target: issue2) }
let(:search_user) { user }
let(:params) { {} }
let(:issues) { IssuesFinder.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute }
+ before do
+ project1.team << [user, :master]
+ project2.team << [user, :developer]
+ project2.team << [user2, :developer]
+
+ issue1
+ issue2
+ issue3
+ end
+
context 'scope: all' do
let(:scope) { 'all' }
@@ -193,6 +193,15 @@ describe IssuesFinder do
expect(issues).to contain_exactly(issue2, issue3)
end
end
+
+ it 'finds issues user can access due to group' do
+ group = create(:group)
+ project = create(:empty_project, group: group)
+ issue = create(:issue, project: project)
+ group.add_user(user, :owner)
+
+ expect(issues).to include(issue)
+ end
end
context 'personal scope' do
@@ -210,5 +219,43 @@ describe IssuesFinder do
end
end
end
+
+ context 'when project restricts issues' do
+ let(:scope) { nil }
+
+ it "doesn't return team-only issues to non team members" do
+ project = create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE)
+ issue = create(:issue, project: project)
+
+ expect(issues).not_to include(issue)
+ end
+
+ it "doesn't return issues if feature disabled" do
+ [project1, project2].each do |project|
+ project.project_feature.update!(issues_access_level: ProjectFeature::DISABLED)
+ end
+
+ expect(issues.count).to eq 0
+ end
+ end
+ end
+
+ describe '.not_restricted_by_confidentiality' do
+ let(:authorized_user) { create(:user) }
+ let(:project) { create(:empty_project, namespace: authorized_user.namespace) }
+ let!(:public_issue) { create(:issue, project: project) }
+ let!(:confidential_issue) { create(:issue, project: project, confidential: true) }
+
+ it 'returns non confidential issues for nil user' do
+ expect(IssuesFinder.send(:not_restricted_by_confidentiality, nil)).to include(public_issue)
+ end
+
+ it 'returns non confidential issues for user not authorized for the issues projects' do
+ expect(IssuesFinder.send(:not_restricted_by_confidentiality, user)).to include(public_issue)
+ end
+
+ it 'returns all issues for user authorized for the issues projects' do
+ expect(IssuesFinder.send(:not_restricted_by_confidentiality, authorized_user)).to include(public_issue, confidential_issue)
+ end
end
end
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 7c6860372cc..4d21254323c 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -2,59 +2,203 @@ require 'spec_helper'
describe NotesFinder do
let(:user) { create :user }
- let(:project) { create :project }
- let(:note1) { create :note_on_commit, project: project }
- let(:note2) { create :note_on_commit, project: project }
- let(:commit) { note1.noteable }
+ let(:project) { create(:empty_project) }
before do
project.team << [user, :master]
end
describe '#execute' do
- let(:params) { { target_id: commit.id, target_type: 'commit', last_fetched_at: 1.hour.ago.to_i } }
+ it 'finds notes on snippets when project is public and user isnt a member'
- before do
- note1
- note2
+ it 'finds notes on merge requests' do
+ create(:note_on_merge_request, project: project)
+
+ notes = described_class.new(project, user).execute
+
+ expect(notes.count).to eq(1)
end
- it 'finds all notes' do
- notes = NotesFinder.new.execute(project, user, params)
- expect(notes.size).to eq(2)
+ it 'finds notes on snippets' do
+ create(:note_on_project_snippet, project: project)
+
+ notes = described_class.new(project, user).execute
+
+ expect(notes.count).to eq(1)
end
- it 'raises an exception for an invalid target_type' do
- params.merge!(target_type: 'invalid')
- expect { NotesFinder.new.execute(project, user, params) }.to raise_error('invalid target_type')
+ it "excludes notes on commits the author can't download" do
+ project = create(:project, :private)
+ note = create(:note_on_commit, project: project)
+ params = { target_type: 'commit', target_id: note.noteable.id }
+
+ notes = described_class.new(project, create(:user), params).execute
+
+ expect(notes.count).to eq(0)
end
- it 'filters out old notes' do
- note2.update_attribute(:updated_at, 2.hours.ago)
- notes = NotesFinder.new.execute(project, user, params)
- expect(notes).to eq([note1])
+ it 'succeeds when no notes found' do
+ notes = described_class.new(project, create(:user)).execute
+
+ expect(notes.count).to eq(0)
end
- context 'confidential issue notes' do
- let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
- let!(:confidential_note) { create(:note, noteable: confidential_issue, project: confidential_issue.project) }
+ context 'on restricted projects' do
+ let(:project) do
+ create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE,
+ snippets_access_level: ProjectFeature::PRIVATE,
+ merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'publicly excludes notes on merge requests' do
+ create(:note_on_merge_request, project: project)
+
+ notes = described_class.new(project, create(:user)).execute
+
+ expect(notes.count).to eq(0)
+ end
+
+ it 'publicly excludes notes on issues' do
+ create(:note_on_issue, project: project)
+
+ notes = described_class.new(project, create(:user)).execute
+
+ expect(notes.count).to eq(0)
+ end
+
+ it 'publicly excludes notes on snippets' do
+ create(:note_on_project_snippet, project: project)
+
+ notes = described_class.new(project, create(:user)).execute
+
+ expect(notes.count).to eq(0)
+ end
+ end
+
+ context 'for target' do
+ let(:project) { create(:project) }
+ let(:note1) { create :note_on_commit, project: project }
+ let(:note2) { create :note_on_commit, project: project }
+ let(:commit) { note1.noteable }
+ let(:params) { { target_id: commit.id, target_type: 'commit', last_fetched_at: 1.hour.ago.to_i } }
+
+ before do
+ note1
+ note2
+ end
+
+ it 'finds all notes' do
+ notes = described_class.new(project, user, params).execute
+ expect(notes.size).to eq(2)
+ end
+
+ it 'finds notes on merge requests' do
+ note = create(:note_on_merge_request, project: project)
+ params = { target_type: 'merge_request', target_id: note.noteable.id }
+
+ notes = described_class.new(project, user, params).execute
+
+ expect(notes).to include(note)
+ end
+
+ it 'finds notes on snippets' do
+ note = create(:note_on_project_snippet, project: project)
+ params = { target_type: 'snippet', target_id: note.noteable.id }
- let(:params) { { target_id: confidential_issue.id, target_type: 'issue', last_fetched_at: 1.hour.ago.to_i } }
+ notes = described_class.new(project, user, params).execute
- it 'returns notes if user can see the issue' do
- expect(NotesFinder.new.execute(project, user, params)).to eq([confidential_note])
+ expect(notes.count).to eq(1)
end
- it 'raises an error if user can not see the issue' do
+ it 'raises an exception for an invalid target_type' do
+ params.merge!(target_type: 'invalid')
+ expect { described_class.new(project, user, params).execute }.to raise_error('invalid target_type')
+ end
+
+ it 'filters out old notes' do
+ note2.update_attribute(:updated_at, 2.hours.ago)
+ notes = described_class.new(project, user, params).execute
+ expect(notes).to eq([note1])
+ end
+
+ context 'confidential issue notes' do
+ let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
+ let!(:confidential_note) { create(:note, noteable: confidential_issue, project: confidential_issue.project) }
+
+ let(:params) { { target_id: confidential_issue.id, target_type: 'issue', last_fetched_at: 1.hour.ago.to_i } }
+
+ it 'returns notes if user can see the issue' do
+ expect(described_class.new(project, user, params).execute).to eq([confidential_note])
+ end
+
+ it 'raises an error if user can not see the issue' do
+ user = create(:user)
+ expect { described_class.new(project, user, params).execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises an error for project members with guest role' do
+ user = create(:user)
+ project.team << [user, :guest]
+
+ expect { described_class.new(project, user, params).execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
+ end
+
+ describe '.search' do
+ let(:project) { create(:empty_project, :public) }
+ let(:note) { create(:note_on_issue, note: 'WoW', project: project) }
+
+ it 'returns notes with matching content' do
+ expect(described_class.new(note.project, nil, search: note.note).execute).to eq([note])
+ end
+
+ it 'returns notes with matching content regardless of the casing' do
+ expect(described_class.new(note.project, nil, search: 'WOW').execute).to eq([note])
+ end
+
+ it 'returns commit notes user can access' do
+ note = create(:note_on_commit, project: project)
+
+ expect(described_class.new(note.project, create(:user), search: note.note).execute).to eq([note])
+ end
+
+ context "confidential issues" do
+ let(:user) { create(:user) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
+ let(:confidential_note) { create(:note, note: "Random", noteable: confidential_issue, project: confidential_issue.project) }
+
+ it "returns notes with matching content if user can see the issue" do
+ expect(described_class.new(confidential_note.project, user, search: confidential_note.note).execute).to eq([confidential_note])
+ end
+
+ it "does not return notes with matching content if user can not see the issue" do
user = create(:user)
- expect { NotesFinder.new.execute(project, user, params) }.to raise_error(ActiveRecord::RecordNotFound)
+ expect(described_class.new(confidential_note.project, user, search: confidential_note.note).execute).to be_empty
end
- it 'raises an error for project members with guest role' do
+ it "does not return notes with matching content for project members with guest role" do
user = create(:user)
project.team << [user, :guest]
+ expect(described_class.new(confidential_note.project, user, search: confidential_note.note).execute).to be_empty
+ end
+
+ it "does not return notes with matching content for unauthenticated users" do
+ expect(described_class.new(confidential_note.project, nil, search: confidential_note.note).execute).to be_empty
+ end
+ end
+
+ context 'inlines SQL filters on subqueries for performance' do
+ let(:sql) { described_class.new(note.project, nil, search: note.note).execute.to_sql }
+ let(:number_of_noteable_types) { 4 }
+
+ specify 'project_id check' do
+ expect(sql.scan(/project_id/).count).to be >= (number_of_noteable_types + 2)
+ end
- expect { NotesFinder.new.execute(project, user, params) }.to raise_error(ActiveRecord::RecordNotFound)
+ specify 'search filter' do
+ expect(sql.scan(/LIKE/).count).to be >= number_of_noteable_types
end
end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 15863d444f8..92053e5a7c6 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe ApplicationHelper do
+ include UploadHelpers
+
describe 'current_controller?' do
it 'returns true when controller matches argument' do
stub_controller_name('foo')
@@ -52,10 +54,8 @@ describe ApplicationHelper do
end
describe 'project_icon' do
- let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') }
-
it 'returns an url for the avatar' do
- project = create(:project, avatar: File.open(avatar_file_path))
+ project = create(:project, avatar: File.open(uploaded_image_temp_path))
avatar_url = "http://#{Gitlab.config.gitlab.host}/uploads/project/avatar/#{project.id}/banana_sample.gif"
expect(helper.project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s).
@@ -74,10 +74,8 @@ describe ApplicationHelper do
end
describe 'avatar_icon' do
- let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') }
-
it 'returns an url for the avatar' do
- user = create(:user, avatar: File.open(avatar_file_path))
+ user = create(:user, avatar: File.open(uploaded_image_temp_path))
expect(helper.avatar_icon(user.email).to_s).
to match("/uploads/user/avatar/#{user.id}/banana_sample.gif")
@@ -88,7 +86,7 @@ describe ApplicationHelper do
# Must be stubbed after the stub above, and separately
stub_config_setting(url: Settings.send(:build_gitlab_url))
- user = create(:user, avatar: File.open(avatar_file_path))
+ user = create(:user, avatar: File.open(uploaded_image_temp_path))
expect(helper.avatar_icon(user.email).to_s).
to match("/gitlab/uploads/user/avatar/#{user.id}/banana_sample.gif")
@@ -102,7 +100,7 @@ describe ApplicationHelper do
describe 'using a User' do
it 'returns an URL for the avatar' do
- user = create(:user, avatar: File.open(avatar_file_path))
+ user = create(:user, avatar: File.open(uploaded_image_temp_path))
expect(helper.avatar_icon(user).to_s).
to match("/uploads/user/avatar/#{user.id}/banana_sample.gif")
diff --git a/spec/javascripts/abuse_reports_spec.js.es6 b/spec/javascripts/abuse_reports_spec.js.es6
index a3171353bfb..9e94c9d1d74 100644
--- a/spec/javascripts/abuse_reports_spec.js.es6
+++ b/spec/javascripts/abuse_reports_spec.js.es6
@@ -1,6 +1,6 @@
-/* eslint-disable */
-/*= require abuse_reports */
+/* eslint-disable space-before-function-paren, no-new, padded-blocks */
+/*= require abuse_reports */
/*= require jquery */
((global) => {
diff --git a/spec/javascripts/activities_spec.js.es6 b/spec/javascripts/activities_spec.js.es6
index 8640cd44085..192da4ee8d9 100644
--- a/spec/javascripts/activities_spec.js.es6
+++ b/spec/javascripts/activities_spec.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-unused-expressions, comma-spacing, prefer-const, no-prototype-builtins, semi, no-new, keyword-spacing, no-plusplus, no-shadow, max-len */
+
/*= require js.cookie.js */
/*= require jquery.endless-scroll.js */
/*= require pager */
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index 7b2e3db6218..89201c8cb8b 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -1,4 +1,5 @@
-/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, no-undef, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, padded-blocks, max-len */
+/* global AwardsHandler */
/*= require awards_handler */
/*= require jquery */
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js
index efb1203eb2f..0f61000bc37 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js
+++ b/spec/javascripts/behaviors/quick_submit_spec.js
@@ -1,4 +1,4 @@
-/* eslint-disable space-before-function-paren, no-var, no-return-assign, comma-dangle, no-undef, jasmine/no-spec-dupes, new-cap, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-var, no-return-assign, comma-dangle, jasmine/no-spec-dupes, new-cap, padded-blocks, max-len */
/*= require behaviors/quick_submit */
diff --git a/spec/javascripts/boards/boards_store_spec.js.es6 b/spec/javascripts/boards/boards_store_spec.js.es6
index b84dfc8197b..b3a1afa28a5 100644
--- a/spec/javascripts/boards/boards_store_spec.js.es6
+++ b/spec/javascripts/boards/boards_store_spec.js.es6
@@ -1,4 +1,11 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, one-var, no-unused-vars, indent */
+/* global Vue */
+/* global BoardService */
+/* global boardsMockInterceptor */
+/* global Cookies */
+/* global listObj */
+/* global listObjDuplicate */
+
//= require jquery
//= require jquery_ujs
//= require js.cookie
diff --git a/spec/javascripts/boards/issue_spec.js.es6 b/spec/javascripts/boards/issue_spec.js.es6
index 90cb8926545..c8a61a0a9b5 100644
--- a/spec/javascripts/boards/issue_spec.js.es6
+++ b/spec/javascripts/boards/issue_spec.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle */
+/* global BoardService */
+/* global ListIssue */
+
//= require jquery
//= require jquery_ujs
//= require js.cookie
diff --git a/spec/javascripts/boards/list_spec.js.es6 b/spec/javascripts/boards/list_spec.js.es6
index dfbcbe3a7c1..7d942ec3d65 100644
--- a/spec/javascripts/boards/list_spec.js.es6
+++ b/spec/javascripts/boards/list_spec.js.es6
@@ -1,4 +1,10 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle */
+/* global Vue */
+/* global boardsMockInterceptor */
+/* global BoardService */
+/* global List */
+/* global listObj */
+
//= require jquery
//= require jquery_ujs
//= require js.cookie
diff --git a/spec/javascripts/boards/mock_data.js.es6 b/spec/javascripts/boards/mock_data.js.es6
index fcb3d8f17d8..8d3e2237fda 100644
--- a/spec/javascripts/boards/mock_data.js.es6
+++ b/spec/javascripts/boards/mock_data.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, no-unused-vars, quote-props */
+
const listObj = {
id: 1,
position: 0,
diff --git a/spec/javascripts/dashboard_spec.js.es6 b/spec/javascripts/dashboard_spec.js.es6
index 93f73fa0e9a..aadf6f518a8 100644
--- a/spec/javascripts/dashboard_spec.js.es6
+++ b/spec/javascripts/dashboard_spec.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable no-new, padded-blocks */
+
/*= require sidebar */
/*= require jquery */
/*= require js.cookie */
diff --git a/spec/javascripts/datetime_utility_spec.js.es6 b/spec/javascripts/datetime_utility_spec.js.es6
index 9fdbab3a9e9..8ece24555c5 100644
--- a/spec/javascripts/datetime_utility_spec.js.es6
+++ b/spec/javascripts/datetime_utility_spec.js.es6
@@ -1,5 +1,5 @@
-/* eslint-disable */
//= require lib/utils/datetime_utility
+
(() => {
describe('Date time utils', () => {
describe('get day name', () => {
diff --git a/spec/javascripts/diff_comments_store_spec.js.es6 b/spec/javascripts/diff_comments_store_spec.js.es6
index 9b2845af608..18805d26ac0 100644
--- a/spec/javascripts/diff_comments_store_spec.js.es6
+++ b/spec/javascripts/diff_comments_store_spec.js.es6
@@ -1,8 +1,11 @@
-/* eslint-disable */
+/* eslint-disable no-extra-semi, jasmine/no-global-setup, dot-notation, jasmine/no-expect-in-setup-teardown, max-len */
+/* global CommentsStore */
+
//= require vue
//= require diff_notes/models/discussion
//= require diff_notes/models/note
//= require diff_notes/stores/comments
+
(() => {
function createDiscussion(noteId = 1, resolved = true) {
CommentsStore.create('a', noteId, true, resolved, 'test');
diff --git a/spec/javascripts/extensions/object_spec.js.es6 b/spec/javascripts/extensions/object_spec.js.es6
new file mode 100644
index 00000000000..3b71c255b30
--- /dev/null
+++ b/spec/javascripts/extensions/object_spec.js.es6
@@ -0,0 +1,25 @@
+/*= require extensions/object */
+
+describe('Object extensions', () => {
+ describe('assign', () => {
+ it('merges source object into target object', () => {
+ const targetObj = {};
+ const sourceObj = {
+ foo: 'bar',
+ };
+ Object.assign(targetObj, sourceObj);
+ expect(targetObj.foo).toBe('bar');
+ });
+
+ it('merges object with the same properties', () => {
+ const targetObj = {
+ foo: 'bar',
+ };
+ const sourceObj = {
+ foo: 'baz',
+ };
+ Object.assign(targetObj, sourceObj);
+ expect(targetObj.foo).toBe('baz');
+ });
+ });
+});
diff --git a/spec/javascripts/gl_dropdown_spec.js.es6 b/spec/javascripts/gl_dropdown_spec.js.es6
index 8ba238018cd..bfaf90e2aee 100644
--- a/spec/javascripts/gl_dropdown_spec.js.es6
+++ b/spec/javascripts/gl_dropdown_spec.js.es6
@@ -1,4 +1,6 @@
-/* eslint-disable */
+/* eslint-disable comma-dangle, prefer-const, no-param-reassign, no-plusplus, semi, no-unused-expressions, arrow-spacing, max-len */
+/* global Turbolinks */
+
/*= require jquery */
/*= require gl_dropdown */
/*= require turbolinks */
diff --git a/spec/javascripts/gl_field_errors_spec.js.es6 b/spec/javascripts/gl_field_errors_spec.js.es6
index 0713e30e485..5018e87ad6c 100644
--- a/spec/javascripts/gl_field_errors_spec.js.es6
+++ b/spec/javascripts/gl_field_errors_spec.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable space-before-function-paren, arrow-body-style, indent, padded-blocks */
+
//= require jquery
//= require gl_field_errors
diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
index a406e6cc36a..bc5cbeb6a40 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
@@ -1,4 +1,8 @@
-/* eslint-disable quotes, no-undef, indent, semi, object-curly-spacing, jasmine/no-suite-dupes, vars-on-top, no-var, padded-blocks, spaced-comment, max-len */
+/* eslint-disable quotes, indent, semi, object-curly-spacing, jasmine/no-suite-dupes, vars-on-top, no-var, padded-blocks, spaced-comment, max-len */
+/* global d3 */
+/* global ContributorsGraph */
+/* global ContributorsMasterGraph */
+
//= require graphs/stat_graph_contributors_graph
describe("ContributorsGraph", function () {
diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
index 96f39abe13e..751f3d175e2 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
@@ -1,4 +1,6 @@
-/* eslint-disable quotes, padded-blocks, no-var, camelcase, object-curly-spacing, semi, indent, object-property-newline, comma-dangle, comma-spacing, no-undef, spaced-comment, max-len, key-spacing, vars-on-top, quote-props, no-multi-spaces, max-len */
+/* eslint-disable quotes, padded-blocks, no-var, camelcase, object-curly-spacing, semi, indent, object-property-newline, comma-dangle, comma-spacing, spaced-comment, max-len, key-spacing, vars-on-top, quote-props, no-multi-spaces */
+/* global ContributorsStatGraphUtil */
+
//= require graphs/stat_graph_contributors_util
describe("ContributorsStatGraphUtil", function () {
diff --git a/spec/javascripts/graphs/stat_graph_spec.js b/spec/javascripts/graphs/stat_graph_spec.js
index f78573b992b..0da124632ae 100644
--- a/spec/javascripts/graphs/stat_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_spec.js
@@ -1,4 +1,6 @@
-/* eslint-disable quotes, padded-blocks, no-undef, semi */
+/* eslint-disable quotes, padded-blocks, semi */
+/* global StatGraph */
+
//= require graphs/stat_graph
describe("StatGraph", function () {
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index 14af6644de1..faab5ae00c2 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -1,4 +1,5 @@
-/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-use-before-define, indent, no-undef, no-trailing-spaces, comma-dangle, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-use-before-define, indent, no-trailing-spaces, comma-dangle, padded-blocks, max-len */
+/* global Issue */
/*= require lib/utils/text_utility */
/*= require issue */
@@ -70,7 +71,7 @@
$('input[type=checkbox]').attr('checked', true).trigger('change');
expect($('.js-task-list-field').val()).toBe('- [x] Task List Item');
});
-
+
it('submits an ajax request on tasklist:changed', function() {
spyOn(jQuery, 'ajax').and.callFake(function(req) {
expect(req.type).toBe('PATCH');
diff --git a/spec/javascripts/labels_issue_sidebar_spec.js.es6 b/spec/javascripts/labels_issue_sidebar_spec.js.es6
index 49687048eb5..0c48d04776f 100644
--- a/spec/javascripts/labels_issue_sidebar_spec.js.es6
+++ b/spec/javascripts/labels_issue_sidebar_spec.js.es6
@@ -1,4 +1,7 @@
-/* eslint-disable */
+/* eslint-disable no-new, no-plusplus, object-curly-spacing, prefer-const, semi */
+/* global IssuableContext */
+/* global LabelsSelect */
+
//= require lib/utils/type_utility
//= require jquery
//= require bootstrap
diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js
index b8b174a2e53..decdf583410 100644
--- a/spec/javascripts/line_highlighter_spec.js
+++ b/spec/javascripts/line_highlighter_spec.js
@@ -1,4 +1,5 @@
-/* eslint-disable space-before-function-paren, no-var, no-param-reassign, quotes, prefer-template, no-else-return, new-cap, dot-notation, no-undef, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, no-plusplus, jasmine/no-spec-dupes, no-underscore-dangle, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-var, no-param-reassign, quotes, prefer-template, no-else-return, new-cap, dot-notation, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, no-plusplus, jasmine/no-spec-dupes, no-underscore-dangle, padded-blocks, max-len */
+/* global LineHighlighter */
/*= require line_highlighter */
diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js
index cbe2634d3a4..4cf1693af1b 100644
--- a/spec/javascripts/merge_request_spec.js
+++ b/spec/javascripts/merge_request_spec.js
@@ -1,4 +1,5 @@
-/* eslint-disable space-before-function-paren, no-return-assign, no-undef, padded-blocks */
+/* eslint-disable space-before-function-paren, no-return-assign, padded-blocks */
+/* global MergeRequest */
/*= require merge_request */
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index 8828970d984..a6cb9e47744 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -1,4 +1,5 @@
-/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, no-undef, quotes, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, padded-blocks, max-len */
+/* global NewBranchForm */
/*= require jquery-ui/autocomplete */
/*= require new_branch_form */
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 2db182d702b..d3bfa7730fa 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -1,4 +1,6 @@
-/* eslint-disable space-before-function-paren, no-unused-expressions, no-undef, no-var, object-shorthand, comma-dangle, semi, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-unused-expressions, no-var, object-shorthand, comma-dangle, semi, padded-blocks, max-len */
+/* global Notes */
+
/*= require notes */
/*= require autosize */
/*= require gl_form */
diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js
index 65de1756201..bb802a4b5e3 100644
--- a/spec/javascripts/project_title_spec.js
+++ b/spec/javascripts/project_title_spec.js
@@ -1,4 +1,6 @@
-/* eslint-disable space-before-function-paren, no-unused-expressions, no-return-assign, no-undef, no-param-reassign, no-var, new-cap, wrap-iife, no-unused-vars, quotes, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-unused-expressions, no-return-assign, no-param-reassign, no-var, new-cap, wrap-iife, no-unused-vars, quotes, jasmine/no-expect-in-setup-teardown, padded-blocks, max-len */
+
+/* global Project */
/*= require bootstrap */
/*= require select2 */
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 0a9bc546144..a083dbf033a 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -1,4 +1,5 @@
-/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, new-parens, no-undef, no-return-assign, new-cap, vars-on-top, semi, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, new-parens, no-return-assign, new-cap, vars-on-top, semi, padded-blocks, max-len */
+/* global Sidebar */
/*= require right_sidebar */
/*= require jquery */
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index e37816b0a8c..7bc898aed5d 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -1,4 +1,5 @@
-/* eslint-disable space-before-function-paren, no-return-assign, no-undef, no-var, quotes, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-return-assign, no-var, quotes, padded-blocks */
+/* global ShortcutsIssuable */
/*= require shortcuts_issuable */
diff --git a/spec/javascripts/subbable_resource_spec.js.es6 b/spec/javascripts/subbable_resource_spec.js.es6
index df395296791..6a70dd856a7 100644
--- a/spec/javascripts/subbable_resource_spec.js.es6
+++ b/spec/javascripts/subbable_resource_spec.js.es6
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable max-len, arrow-parens, comma-dangle, no-plusplus */
+
//= vue
//= vue-resource
//= require jquery
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index 944df6d23f7..a8874ab12d3 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -1,4 +1,6 @@
-/* eslint-disable space-before-function-paren, new-parens, no-undef, quotes, comma-dangle, no-var, one-var, one-var-declaration-per-line, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, new-parens, quotes, comma-dangle, no-var, one-var, one-var-declaration-per-line, padded-blocks, max-len */
+/* global MockU2FDevice */
+/* global U2FAuthenticate */
/*= require u2f/authenticate */
/*= require u2f/util */
diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js
index 0c73c5772bd..189592ea87a 100644
--- a/spec/javascripts/u2f/register_spec.js
+++ b/spec/javascripts/u2f/register_spec.js
@@ -1,4 +1,6 @@
-/* eslint-disable space-before-function-paren, new-parens, no-undef, quotes, no-var, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, new-parens, quotes, no-var, one-var, one-var-declaration-per-line, comma-dangle, padded-blocks, max-len */
+/* global MockU2FDevice */
+/* global U2FRegister */
/*= require u2f/register */
/*= require u2f/util */
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index b9acaaa5a0d..d80ce5a7f7e 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -1,4 +1,7 @@
-/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-undef, object-shorthand, comma-dangle, no-return-assign, new-cap, padded-blocks, max-len */
+/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, no-return-assign, new-cap, padded-blocks, max-len */
+/* global Dropzone */
+/* global Mousetrap */
+/* global ZenMode */
/*= require zen_mode */
diff --git a/spec/lib/banzai/filter/math_filter_spec.rb b/spec/lib/banzai/filter/math_filter_spec.rb
new file mode 100644
index 00000000000..3fe2c7f5d5d
--- /dev/null
+++ b/spec/lib/banzai/filter/math_filter_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+
+describe Banzai::Filter::MathFilter, lib: true do
+ include FilterSpecHelper
+
+ it 'leaves regular inline code unchanged' do
+ input = "<code>2+2</code>"
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'removes surrounding dollar signs and adds class code, math and js-render-math' do
+ doc = filter("$<code>2+2</code>$")
+
+ expect(doc.to_s).to eq '<code class="code math js-render-math" data-math-style="inline">2+2</code>'
+ end
+
+ it 'only removes surrounding dollar signs' do
+ doc = filter("test $<code>2+2</code>$ test")
+ before = doc.xpath('descendant-or-self::text()[1]').first
+ after = doc.xpath('descendant-or-self::text()[3]').first
+
+ expect(before.to_s).to eq 'test '
+ expect(after.to_s).to eq ' test'
+ end
+
+ it 'only removes surrounding single dollar sign' do
+ doc = filter("test $$<code>2+2</code>$$ test")
+ before = doc.xpath('descendant-or-self::text()[1]').first
+ after = doc.xpath('descendant-or-self::text()[3]').first
+
+ expect(before.to_s).to eq 'test $'
+ expect(after.to_s).to eq '$ test'
+ end
+
+ it 'adds data-math-style inline attribute to inline math' do
+ doc = filter('$<code>2+2</code>$')
+ code = doc.xpath('descendant-or-self::code').first
+
+ expect(code['data-math-style']).to eq 'inline'
+ end
+
+ it 'adds class code and math to inline math' do
+ doc = filter('$<code>2+2</code>$')
+ code = doc.xpath('descendant-or-self::code').first
+
+ expect(code[:class]).to include("code")
+ expect(code[:class]).to include("math")
+ end
+
+ it 'adds js-render-math class to inline math' do
+ doc = filter('$<code>2+2</code>$')
+ code = doc.xpath('descendant-or-self::code').first
+
+ expect(code[:class]).to include("js-render-math")
+ end
+
+ # Cases with faulty syntax. Should be a no-op
+
+ it 'ignores cases with missing dolar sign at the end' do
+ input = "test $<code>2+2</code> test"
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'ignores cases with missing dolar sign at the beginning' do
+ input = "test <code>2+2</code>$ test"
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'ignores dollar signs if it is not adjacent' do
+ input = '<p>We check strictly $<code>2+2</code> and <code>2+2</code>$ </p>'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ # Display math
+
+ it 'adds data-math-style display attribute to display math' do
+ doc = filter('<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>')
+ pre = doc.xpath('descendant-or-self::pre').first
+
+ expect(pre['data-math-style']).to eq 'display'
+ end
+
+ it 'adds js-render-math class to display math' do
+ doc = filter('<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>')
+ pre = doc.xpath('descendant-or-self::pre').first
+
+ expect(pre[:class]).to include("js-render-math")
+ end
+
+ it 'ignores code blocks that are not math' do
+ input = '<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>2+2</code></pre>'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'requires the pre to contain both code and math' do
+ input = '<pre class="highlight js-syntax-highlight plaintext math" v-pre="true"><code>2+2</code></pre>'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'dollar signs around to display math' do
+ doc = filter('$<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>$')
+ before = doc.xpath('descendant-or-self::text()[1]').first
+ after = doc.xpath('descendant-or-self::text()[3]').first
+
+ expect(before.to_s).to eq '$'
+ expect(after.to_s).to eq '$'
+ end
+end
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 4aba783dc33..f3843ca64ff 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -11,7 +11,7 @@ module Gitlab
it "converts the input using Asciidoctor and default options" do
expected_asciidoc_opts = {
safe: :secure,
- backend: :html5,
+ backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS
}
@@ -27,7 +27,7 @@ module Gitlab
it "merges the options with default ones" do
expected_asciidoc_opts = {
safe: :safe,
- backend: :html5,
+ backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo']
}
diff --git a/spec/lib/gitlab/badge/build/status_spec.rb b/spec/lib/gitlab/badge/build/status_spec.rb
index 38eebb2a176..70f03021d36 100644
--- a/spec/lib/gitlab/badge/build/status_spec.rb
+++ b/spec/lib/gitlab/badge/build/status_spec.rb
@@ -69,8 +69,8 @@ describe Gitlab::Badge::Build::Status do
new_build.success!
end
- it 'reports the compound status' do
- expect(badge.status).to eq 'failed'
+ it 'does not take outdated pipeline into account' do
+ expect(badge.status).to eq 'success'
end
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 8e1a28f2723..c4ee838b7c9 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -147,6 +147,7 @@ project:
- bugzilla_service
- gitlab_issue_tracker_service
- external_wiki_service
+- kubernetes_service
- forked_project_link
- forked_from_project
- forked_project_links
diff --git a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
index 5ae178414cc..08a42fd27a2 100644
--- a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
@@ -1,12 +1,14 @@
require 'spec_helper'
describe Gitlab::ImportExport::AvatarRestorer, lib: true do
+ include UploadHelpers
+
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
let(:project) { create(:empty_project) }
before do
allow_any_instance_of(described_class).to receive(:avatar_export_file)
- .and_return(Rails.root + "spec/fixtures/dk.png")
+ .and_return(uploaded_image_temp_path)
end
after do
diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb
new file mode 100644
index 00000000000..c79c6494576
--- /dev/null
+++ b/spec/lib/gitlab/middleware/multipart_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+
+require 'tempfile'
+
+describe Gitlab::Middleware::Multipart do
+ let(:app) { double(:app) }
+ let(:middleware) { described_class.new(app) }
+
+ it 'opens top-level files' do
+ Tempfile.open do |tempfile|
+ env = post_env({ 'file' => tempfile.path }, { 'file.name' => 'filename' }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect(app).to receive(:call) do |env|
+ file = Rack::Request.new(env).params['file']
+ expect(file).to be_a(File)
+ expect(file.path).to eq(tempfile.path)
+ end
+
+ middleware.call(env)
+ end
+ end
+
+ it 'rejects headers signed with the wrong secret' do
+ env = post_env({ 'file' => '/var/empty/nonesuch' }, {}, 'x' * 32, 'gitlab-workhorse')
+
+ expect { middleware.call(env) }.to raise_error(JWT::VerificationError)
+ end
+
+ it 'rejects headers signed with the wrong issuer' do
+ env = post_env({ 'file' => '/var/empty/nonesuch' }, {}, Gitlab::Workhorse.secret, 'acme-inc')
+
+ expect { middleware.call(env) }.to raise_error(JWT::InvalidIssuerError)
+ end
+
+ it 'opens files one level deep' do
+ Tempfile.open do |tempfile|
+ in_params = { 'user' => { 'avatar' => { '.name' => 'filename' } } }
+ env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect(app).to receive(:call) do |env|
+ file = Rack::Request.new(env).params['user']['avatar']
+ expect(file).to be_a(File)
+ expect(file.path).to eq(tempfile.path)
+ end
+
+ middleware.call(env)
+ end
+ end
+
+ it 'opens files two levels deep' do
+ Tempfile.open do |tempfile|
+ in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => 'filename' } } } }
+ env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect(app).to receive(:call) do |env|
+ file = Rack::Request.new(env).params['project']['milestone']['themesong']
+ expect(file).to be_a(File)
+ expect(file.path).to eq(tempfile.path)
+ end
+
+ middleware.call(env)
+ end
+ end
+
+ def post_env(rewritten_fields, params, secret, issuer)
+ token = JWT.encode({ 'iss' => issuer, 'rewritten_fields' => rewritten_fields }, secret, 'HS256')
+ Rack::MockRequest.env_for(
+ '/',
+ method: 'post',
+ params: params,
+ described_class::RACK_ENV_KEY => token
+ )
+ end
+end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 3cd9863ec6a..14ee386dba6 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -149,4 +149,33 @@ describe Gitlab::ProjectSearchResults, lib: true do
expect(results.issues_count).to eq 3
end
end
+
+ describe 'notes search' do
+ it 'lists notes' do
+ project = create(:empty_project, :public)
+ note = create(:note, project: project)
+
+ results = described_class.new(user, project, note.note)
+
+ expect(results.objects('notes')).to include note
+ end
+
+ it "doesn't list issue notes when access is restricted" do
+ project = create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE)
+ note = create(:note_on_issue, project: project)
+
+ results = described_class.new(user, project, note.note)
+
+ expect(results.objects('notes')).not_to include note
+ end
+
+ it "doesn't list merge_request notes when access is restricted" do
+ project = create(:empty_project, :public, merge_requests_access_level: ProjectFeature::PRIVATE)
+ note = create(:note_on_merge_request, project: project)
+
+ results = described_class.new(user, project, note.note)
+
+ expect(results.objects('notes')).not_to include note
+ end
+ end
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index c51b10bdc69..c78cd30157e 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -29,4 +29,20 @@ describe Gitlab::Regex, lib: true do
describe 'file path regex' do
it { expect('foo@/bar').to match(Gitlab::Regex.file_path_regex) }
end
+
+ describe 'environment slug regex' do
+ def be_matched
+ match(Gitlab::Regex.environment_slug_regex)
+ end
+
+ it { expect('foo').to be_matched }
+ it { expect('foo-1').to be_matched }
+
+ it { expect('FOO').not_to be_matched }
+ it { expect('foo/1').not_to be_matched }
+ it { expect('foo.1').not_to be_matched }
+ it { expect('foo*1').not_to be_matched }
+ it { expect('9foo').not_to be_matched }
+ it { expect('foo-').not_to be_matched }
+ end
end
diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb
index 0cdbab87544..849edb09476 100644
--- a/spec/lib/gitlab/sql/union_spec.rb
+++ b/spec/lib/gitlab/sql/union_spec.rb
@@ -1,16 +1,26 @@
require 'spec_helper'
describe Gitlab::SQL::Union, lib: true do
+ let(:relation_1) { User.where(email: 'alice@example.com').select(:id) }
+ let(:relation_2) { User.where(email: 'bob@example.com').select(:id) }
+
+ def to_sql(relation)
+ relation.reorder(nil).to_sql
+ end
+
describe '#to_sql' do
it 'returns a String joining relations together using a UNION' do
- rel1 = User.where(email: 'alice@example.com')
- rel2 = User.where(email: 'bob@example.com')
- union = described_class.new([rel1, rel2])
+ union = described_class.new([relation_1, relation_2])
+
+ expect(union.to_sql).to eq("#{to_sql(relation_1)}\nUNION\n#{to_sql(relation_2)}")
+ end
- sql1 = rel1.reorder(nil).to_sql
- sql2 = rel2.reorder(nil).to_sql
+ it 'skips Model.none segements' do
+ empty_relation = User.none
+ union = described_class.new([empty_relation, relation_1, relation_2])
- expect(union.to_sql).to eq("#{sql1}\nUNION\n#{sql2}")
+ expect{User.where("users.id IN (#{union.to_sql})").to_a}.not_to raise_error
+ expect(union.to_sql).to eq("#{to_sql(relation_1)}\nUNION\n#{to_sql(relation_2)}")
end
end
end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 7f39aff7639..d5f2ffcff59 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -87,6 +87,26 @@ describe Ci::Build, models: true do
end
end
+ describe '#persisted_environment' do
+ before do
+ @environment = create(:environment, project: project, name: "foo-#{project.default_branch}")
+ end
+
+ subject { build.persisted_environment }
+
+ context 'referenced literally' do
+ let(:build) { create(:ci_build, pipeline: pipeline, environment: "foo-#{project.default_branch}") }
+
+ it { is_expected.to eq(@environment) }
+ end
+
+ context 'referenced with a variable' do
+ let(:build) { create(:ci_build, pipeline: pipeline, environment: "foo-$CI_BUILD_REF_NAME") }
+
+ it { is_expected.to eq(@environment) }
+ end
+ end
+
describe '#trace' do
it { expect(build.trace).to be_nil }
@@ -254,6 +274,24 @@ describe Ci::Build, models: true do
end
end
+ describe '#ref_slug' do
+ {
+ 'master' => 'master',
+ '1-foo' => '1-foo',
+ 'fix/1-foo' => 'fix-1-foo',
+ 'fix-1-foo' => 'fix-1-foo',
+ 'a' * 63 => 'a' * 63,
+ 'a' * 64 => 'a' * 63,
+ 'FOO' => 'foo',
+ }.each do |ref, slug|
+ it "transforms #{ref} to #{slug}" do
+ build.ref = ref
+
+ expect(build.ref_slug).to eq(slug)
+ end
+ end
+ end
+
describe '#variables' do
let(:container_registry_enabled) { false }
let(:predefined_variables) do
@@ -265,6 +303,7 @@ describe Ci::Build, models: true do
{ key: 'CI_BUILD_REF', value: build.sha, public: true },
{ key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true },
{ key: 'CI_BUILD_REF_NAME', value: 'master', public: true },
+ { key: 'CI_BUILD_REF_SLUG', value: 'master', public: true },
{ key: 'CI_BUILD_NAME', value: 'test', public: true },
{ key: 'CI_BUILD_STAGE', value: 'test', public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
@@ -309,6 +348,22 @@ describe Ci::Build, models: true do
it { user_variables.each { |v| is_expected.to include(v) } }
end
+ context 'when build has an environment' do
+ before do
+ build.update(environment: 'production')
+ create(:environment, project: build.project, name: 'production', slug: 'prod-slug')
+ end
+
+ let(:environment_variables) do
+ [
+ { key: 'CI_ENVIRONMENT_NAME', value: 'production', public: true },
+ { key: 'CI_ENVIRONMENT_SLUG', value: 'prod-slug', public: true }
+ ]
+ end
+
+ it { environment_variables.each { |v| is_expected.to include(v) } }
+ end
+
context 'when build started manually' do
before do
build.update_attributes(when: :manual)
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index e78ae14b737..52dd41065e9 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -381,6 +381,65 @@ describe Ci::Pipeline, models: true do
end
end
+ shared_context 'with some outdated pipelines' do
+ before do
+ create_pipeline(:canceled, 'ref', 'A')
+ create_pipeline(:success, 'ref', 'A')
+ create_pipeline(:failed, 'ref', 'B')
+ create_pipeline(:skipped, 'feature', 'C')
+ end
+
+ def create_pipeline(status, ref, sha)
+ create(:ci_empty_pipeline, status: status, ref: ref, sha: sha)
+ end
+ end
+
+ describe '.latest' do
+ include_context 'with some outdated pipelines'
+
+ context 'when no ref is specified' do
+ let(:pipelines) { described_class.latest.all }
+
+ it 'returns the latest pipeline for the same ref and different sha' do
+ expect(pipelines.map(&:sha)).to contain_exactly('A', 'B', 'C')
+ expect(pipelines.map(&:status)).
+ to contain_exactly('success', 'failed', 'skipped')
+ end
+ end
+
+ context 'when ref is specified' do
+ let(:pipelines) { described_class.latest('ref').all }
+
+ it 'returns the latest pipeline for ref and different sha' do
+ expect(pipelines.map(&:sha)).to contain_exactly('A', 'B')
+ expect(pipelines.map(&:status)).
+ to contain_exactly('success', 'failed')
+ end
+ end
+ end
+
+ describe '.latest_status' do
+ include_context 'with some outdated pipelines'
+
+ context 'when no ref is specified' do
+ let(:latest_status) { described_class.latest_status }
+
+ it 'returns the latest status for the same ref and different sha' do
+ expect(latest_status).to eq(described_class.latest.status)
+ expect(latest_status).to eq('failed')
+ end
+ end
+
+ context 'when ref is specified' do
+ let(:latest_status) { described_class.latest_status('ref') }
+
+ it 'returns the latest status for ref and different sha' do
+ expect(latest_status).to eq(described_class.latest_status('ref'))
+ expect(latest_status).to eq('failed')
+ end
+ end
+ end
+
describe '#status' do
let!(:build) { create(:ci_build, :created, pipeline: pipeline, name: 'test') }
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 0935fc0561c..74b50d2908d 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -213,23 +213,19 @@ eos
end
describe '#status' do
- context 'without arguments for compound status' do
- shared_examples 'giving the status from pipeline' do
- it do
- expect(commit.status).to eq(Ci::Pipeline.status)
- end
- end
-
- context 'with pipelines' do
- let!(:pipeline) do
- create(:ci_empty_pipeline, project: project, sha: commit.sha)
+ context 'without ref argument' do
+ before do
+ %w[success failed created pending].each do |status|
+ create(:ci_empty_pipeline,
+ project: project,
+ sha: commit.sha,
+ status: status)
end
-
- it_behaves_like 'giving the status from pipeline'
end
- context 'without pipelines' do
- it_behaves_like 'giving the status from pipeline'
+ it 'gives compound status from latest pipelines' do
+ expect(commit.status).to eq(Ci::Pipeline.latest_status)
+ expect(commit.status).to eq('pending')
end
end
@@ -255,8 +251,9 @@ eos
expect(commit.status('fix')).to eq(pipeline_from_fix.status)
end
- it 'gives compound status if ref is nil' do
- expect(commit.status(nil)).to eq(commit.status)
+ it 'gives compound status from latest pipelines if ref is nil' do
+ expect(commit.status(nil)).to eq(Ci::Pipeline.latest_status)
+ expect(commit.status(nil)).to eq('failed')
end
end
end
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index eb64f3d0c83..4b0bfa43abf 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -6,6 +6,7 @@ shared_examples 'TokenAuthenticatable' do
it { expect(described_class).to be_private_method_defined(:write_new_token) }
it { expect(described_class).to respond_to("find_by_#{token_field}") }
it { is_expected.to respond_to("ensure_#{token_field}") }
+ it { is_expected.to respond_to("set_#{token_field}") }
it { is_expected.to respond_to("reset_#{token_field}!") }
end
end
@@ -55,6 +56,12 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
end
end
+ describe 'setting new token' do
+ subject { described_class.new.send("set_#{token_field}", '0123456789') }
+
+ it { is_expected.to eq '0123456789' }
+ end
+
describe 'multiple token fields' do
before do
described_class.send(:add_authentication_token_field, :yet_another_token)
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index c8170164898..97cbb093ed2 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Environment, models: true do
- let(:environment) { create(:environment) }
+ subject(:environment) { create(:environment) }
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:deployments) }
@@ -15,15 +15,11 @@ describe Environment, models: true do
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
- it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
-
- # To circumvent a not null violation of the name column:
- # https://github.com/thoughtbot/shoulda-matchers/issues/336
- it 'validates uniqueness of :external_url' do
- create(:environment)
+ it { is_expected.to validate_uniqueness_of(:slug).scoped_to(:project_id) }
+ it { is_expected.to validate_length_of(:slug).is_at_most(24) }
- is_expected.to validate_uniqueness_of(:external_url).scoped_to(:project_id)
- end
+ it { is_expected.to validate_length_of(:external_url).is_at_most(255) }
+ it { is_expected.to validate_uniqueness_of(:external_url).scoped_to(:project_id) }
describe '#nullify_external_url' do
it 'replaces a blank url with nil' do
@@ -199,4 +195,38 @@ describe Environment, models: true do
expect(environment.actions_for('review/master')).to contain_exactly(review_action)
end
end
+
+ describe '#slug' do
+ it "is automatically generated" do
+ expect(environment.slug).not_to be_nil
+ end
+
+ it "is not regenerated if name changes" do
+ original_slug = environment.slug
+ environment.update_attributes!(name: environment.name.reverse)
+
+ expect(environment.slug).to eq(original_slug)
+ end
+ end
+
+ describe '#generate_slug' do
+ SUFFIX = "-[a-z0-9]{6}"
+ {
+ "staging-12345678901234567" => "staging-123456789" + SUFFIX,
+ "9-staging-123456789012345" => "env-9-staging-123" + SUFFIX,
+ "staging-1234567890123456" => "staging-1234567890123456",
+ "production" => "production",
+ "PRODUCTION" => "production" + SUFFIX,
+ "review/1-foo" => "review-1-foo" + SUFFIX,
+ "1-foo" => "env-1-foo" + SUFFIX,
+ "1/foo" => "env-1-foo" + SUFFIX,
+ "foo-" => "foo" + SUFFIX,
+ }.each do |name, matcher|
+ it "returns a slug matching #{matcher}, given #{name}" do
+ slug = described_class.new(name: name).generate_slug
+
+ expect(slug).to match(/\A#{matcher}\z/)
+ end
+ end
+ end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 893c6827a91..7d5ecfbaa64 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -272,7 +272,7 @@ describe Group, models: true do
end
describe 'nested group' do
- subject { create(:group, :nested) }
+ subject { build(:group, :nested) }
it { is_expected.to be_valid }
it { expect(subject.parent).to be_kind_of(Group) }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 24e216329a9..bb56e44db29 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -22,26 +22,6 @@ describe Issue, models: true do
it { is_expected.to have_db_index(:deleted_at) }
end
- describe '.visible_to_user' do
- let(:user) { create(:user) }
- let(:authorized_user) { create(:user) }
- let(:project) { create(:project, namespace: authorized_user.namespace) }
- let!(:public_issue) { create(:issue, project: project) }
- let!(:confidential_issue) { create(:issue, project: project, confidential: true) }
-
- it 'returns non confidential issues for nil user' do
- expect(Issue.visible_to_user(nil).count).to be(1)
- end
-
- it 'returns non confidential issues for user not authorized for the issues projects' do
- expect(Issue.visible_to_user(user).count).to be(1)
- end
-
- it 'returns all issues for user authorized for the issues projects' do
- expect(Issue.visible_to_user(authorized_user).count).to be(2)
- end
- end
-
describe '#to_reference' do
let(:project) { build(:empty_project, name: 'sample-project') }
let(:issue) { build(:issue, iid: 1, project: project) }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 8b730be91fd..1b71d00eb8f 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -205,7 +205,7 @@ describe MergeRequest, models: true do
end
end
- describe "#mr_and_commit_notes" do
+ describe "#related_notes" do
let!(:merge_request) { create(:merge_request) }
before do
@@ -217,7 +217,7 @@ describe MergeRequest, models: true do
it "includes notes for commits" do
expect(merge_request.commits).not_to be_empty
- expect(merge_request.mr_and_commit_notes.count).to eq(2)
+ expect(merge_request.related_notes.count).to eq(2)
end
it "includes notes for commits from target project as well" do
@@ -225,7 +225,7 @@ describe MergeRequest, models: true do
project: merge_request.target_project)
expect(merge_request.commits).not_to be_empty
- expect(merge_request.mr_and_commit_notes.count).to eq(3)
+ expect(merge_request.related_notes.count).to eq(3)
end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 0cc2efae5f9..064f29d2d66 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -24,8 +24,9 @@ describe Milestone, models: true do
it { is_expected.to have_many(:issues) }
end
- let(:milestone) { create(:milestone) }
- let(:issue) { create(:issue) }
+ let(:project) { create(:project, :public) }
+ let(:milestone) { create(:milestone, project: project) }
+ let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
describe "#title" do
@@ -110,8 +111,8 @@ describe Milestone, models: true do
describe :items_count do
before do
- milestone.issues << create(:issue)
- milestone.issues << create(:closed_issue)
+ milestone.issues << create(:issue, project: project)
+ milestone.issues << create(:closed_issue, project: project)
milestone.merge_requests << create(:merge_request)
end
@@ -126,7 +127,7 @@ describe Milestone, models: true do
describe '#total_items_count' do
before do
- create :closed_issue, milestone: milestone
+ create :closed_issue, milestone: milestone, project: project
create :merge_request, milestone: milestone
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 17a15b12dcb..310fecd8a5c 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -162,44 +162,6 @@ describe Note, models: true do
end
end
- describe '.search' do
- let(:note) { create(:note_on_issue, note: 'WoW') }
-
- it 'returns notes with matching content' do
- expect(described_class.search(note.note)).to eq([note])
- end
-
- it 'returns notes with matching content regardless of the casing' do
- expect(described_class.search('WOW')).to eq([note])
- end
-
- context "confidential issues" do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
- let(:confidential_note) { create(:note, note: "Random", noteable: confidential_issue, project: confidential_issue.project) }
-
- it "returns notes with matching content if user can see the issue" do
- expect(described_class.search(confidential_note.note, as_user: user)).to eq([confidential_note])
- end
-
- it "does not return notes with matching content if user can not see the issue" do
- user = create(:user)
- expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
- end
-
- it "does not return notes with matching content for project members with guest role" do
- user = create(:user)
- project.team << [user, :guest]
- expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
- end
-
- it "does not return notes with matching content for unauthenticated users" do
- expect(described_class.search(confidential_note.note)).to be_empty
- end
- end
- end
-
describe "editable?" do
it "returns true" do
note = build(:note)
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
new file mode 100644
index 00000000000..ffb92012b89
--- /dev/null
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -0,0 +1,126 @@
+require 'spec_helper'
+
+describe KubernetesService, models: true do
+ let(:project) { create(:empty_project) }
+
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ end
+
+ describe 'Validations' do
+ context 'when service is active' do
+ before { subject.active = true }
+ it { is_expected.to validate_presence_of(:namespace) }
+ it { is_expected.to validate_presence_of(:api_url) }
+ it { is_expected.to validate_presence_of(:token) }
+
+ context 'namespace format' do
+ before do
+ subject.project = project
+ subject.api_url = "http://example.com"
+ subject.token = "test"
+ end
+
+ {
+ 'foo' => true,
+ '1foo' => true,
+ 'foo1' => true,
+ 'foo-bar' => true,
+ '-foo' => false,
+ 'foo-' => false,
+ 'a' * 63 => true,
+ 'a' * 64 => false,
+ 'a.b' => false,
+ 'a*b' => false,
+ }.each do |namespace, validity|
+ it "should validate #{namespace} as #{validity ? 'valid' : 'invalid'}" do
+ subject.namespace = namespace
+
+ expect(subject.valid?).to eq(validity)
+ end
+ end
+ end
+ end
+
+ context 'when service is inactive' do
+ before { subject.active = false }
+ it { is_expected.not_to validate_presence_of(:namespace) }
+ it { is_expected.not_to validate_presence_of(:api_url) }
+ it { is_expected.not_to validate_presence_of(:token) }
+ end
+ end
+
+ describe '#initialize_properties' do
+ context 'with a project' do
+ it 'defaults to the project name' do
+ expect(described_class.new(project: project).namespace).to eq(project.name)
+ end
+ end
+
+ context 'without a project' do
+ it 'leaves the namespace unset' do
+ expect(described_class.new.namespace).to be_nil
+ end
+ end
+ end
+
+ describe '#test' do
+ let(:project) { create(:kubernetes_project) }
+ let(:service) { project.kubernetes_service }
+ let(:discovery_url) { service.api_url + '/api/v1' }
+
+ # JSON response body from Kubernetes GET /api/v1 request
+ let(:discovery_response) { { "kind" => "APIResourceList", "groupVersion" => "v1", "resources" => [] }.to_json }
+
+ context 'with path prefix in api_url' do
+ let(:discovery_url) { 'https://kubernetes.example.com/prefix/api/v1' }
+
+ before do
+ service.api_url = 'https://kubernetes.example.com/prefix/'
+ end
+
+ it 'tests with the prefix' do
+ WebMock.stub_request(:get, discovery_url).to_return(body: discovery_response)
+
+ expect(service.test[:success]).to be_truthy
+ expect(WebMock).to have_requested(:get, discovery_url).once
+ end
+ end
+
+ context 'with custom CA certificate' do
+ let(:certificate) { "CA PEM DATA" }
+ before do
+ service.update_attributes!(ca_pem: certificate)
+ end
+
+ it 'is added to the certificate store' do
+ cert = double("certificate")
+
+ expect(OpenSSL::X509::Certificate).to receive(:new).with(certificate).and_return(cert)
+ expect_any_instance_of(OpenSSL::X509::Store).to receive(:add_cert).with(cert)
+ WebMock.stub_request(:get, discovery_url).to_return(body: discovery_response)
+
+ expect(service.test[:success]).to be_truthy
+ expect(WebMock).to have_requested(:get, discovery_url).once
+ end
+ end
+
+ context 'success' do
+ it 'reads the discovery endpoint' do
+ WebMock.stub_request(:get, discovery_url).to_return(body: discovery_response)
+
+ expect(service.test[:success]).to be_truthy
+ expect(WebMock).to have_requested(:get, discovery_url).once
+ end
+ end
+
+ context 'failure' do
+ it 'fails to read the discovery endpoint' do
+ WebMock.stub_request(:get, discovery_url).to_return(status: 404)
+
+ expect(service.test[:success]).to be_falsy
+ expect(WebMock).to have_requested(:get, discovery_url).once
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/slack_service/pipeline_message_spec.rb b/spec/models/project_services/slack_service/pipeline_message_spec.rb
index 363138a9454..4098500122f 100644
--- a/spec/models/project_services/slack_service/pipeline_message_spec.rb
+++ b/spec/models/project_services/slack_service/pipeline_message_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe SlackService::PipelineMessage do
subject { SlackService::PipelineMessage.new(args) }
+ let(:user) { { name: 'hacker' } }
let(:args) do
{
@@ -15,7 +16,7 @@ describe SlackService::PipelineMessage do
},
project: { path_with_namespace: 'project_name',
web_url: 'example.gitlab.com' },
- user: { name: 'hacker' }
+ user: user
}
end
@@ -28,9 +29,7 @@ describe SlackService::PipelineMessage do
let(:message) { build_message('passed') }
it 'returns a message with information about succeeded build' do
- expect(subject.pretext).to be_empty
- expect(subject.fallback).to eq(message)
- expect(subject.attachments).to eq([text: message, color: color])
+ verify_message
end
end
@@ -40,16 +39,29 @@ describe SlackService::PipelineMessage do
let(:duration) { 10 }
it 'returns a message with information about failed build' do
- expect(subject.pretext).to be_empty
- expect(subject.fallback).to eq(message)
- expect(subject.attachments).to eq([text: message, color: color])
+ verify_message
end
+
+ context 'when triggered by API therefore lacking user' do
+ let(:user) { nil }
+ let(:message) { build_message(status, 'API') }
+
+ it 'returns a message stating it is by API' do
+ verify_message
+ end
+ end
+ end
+
+ def verify_message
+ expect(subject.pretext).to be_empty
+ expect(subject.fallback).to eq(message)
+ expect(subject.attachments).to eq([text: message, color: color])
end
- def build_message(status_text = status)
+ def build_message(status_text = status, name = user[:name])
"<example.gitlab.com|project_name>:" \
" Pipeline <example.gitlab.com/pipelines/123|#123>" \
" of <example.gitlab.com/commits/develop|develop> branch" \
- " by hacker #{status_text} in #{duration} #{'second'.pluralize(duration)}"
+ " by #{name} #{status_text} in #{duration} #{'second'.pluralize(duration)}"
end
end
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index 126496c43a5..b9d535bc314 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -46,6 +46,7 @@ describe API::Environments, api: true do
expect(response).to have_http_status(201)
expect(json_response['name']).to eq('mepmep')
+ expect(json_response['slug']).to eq('mepmep')
expect(json_response['external']).to be nil
end
@@ -60,6 +61,13 @@ describe API::Environments, api: true do
expect(response).to have_http_status(400)
end
+
+ it 'returns a 400 if slug is specified' do
+ post api("/projects/#{project.id}/environments", user), name: "foo", slug: "foo"
+
+ expect(response).to have_http_status(400)
+ expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
+ end
end
context 'a non member' do
@@ -86,6 +94,15 @@ describe API::Environments, api: true do
expect(json_response['external_url']).to eq(url)
end
+ it "won't allow slug to be changed" do
+ slug = environment.slug
+ api_url = api("/projects/#{project.id}/environments/#{environment.id}", user)
+ put api_url, slug: slug + "-foo"
+
+ expect(response).to have_http_status(400)
+ expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
+ end
+
it "won't update the external_url if only the name is passed" do
url = environment.external_url
put api("/projects/#{project.id}/environments/#{environment.id}", user),
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index a75ba824e85..cdeb965b413 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -2,13 +2,13 @@ require 'spec_helper'
describe API::Groups, api: true do
include ApiHelpers
+ include UploadHelpers
let(:user1) { create(:user, can_create_group: false) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
- let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') }
- let!(:group1) { create(:group, avatar: File.open(avatar_file_path)) }
+ let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) }
let!(:project1) { create(:project, namespace: group1) }
let!(:project2) { create(:project, namespace: group2) }
diff --git a/spec/serializers/analytics_build_entity_spec.rb b/spec/serializers/analytics_build_entity_spec.rb
index 6b33fe66a63..86e703a6448 100644
--- a/spec/serializers/analytics_build_entity_spec.rb
+++ b/spec/serializers/analytics_build_entity_spec.rb
@@ -13,6 +13,14 @@ describe AnalyticsBuildEntity do
subject { entity.as_json }
+ before do
+ Timecop.freeze
+ end
+
+ after do
+ Timecop.return
+ end
+
it 'contains the URL' do
expect(subject).to include(:url)
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 4aadd009f3e..ceaca96e25b 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -210,5 +210,22 @@ describe Ci::CreatePipelineService, services: true do
expect(result.manual_actions).not_to be_empty
end
end
+
+ context 'with environment' do
+ before do
+ config = YAML.dump(deploy: { environment: { name: "review/$CI_BUILD_REF_NAME" }, script: 'ls' })
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'creates the environment' do
+ result = execute(ref: 'refs/heads/master',
+ before: '00000000',
+ after: project.commit.id,
+ commits: [{ message: 'some msg' }])
+
+ expect(result).to be_persisted
+ expect(Environment.find_by(name: "review/master")).not_to be_nil
+ end
+ end
end
end
diff --git a/spec/support/upload_helpers.rb b/spec/support/upload_helpers.rb
new file mode 100644
index 00000000000..5eead80c935
--- /dev/null
+++ b/spec/support/upload_helpers.rb
@@ -0,0 +1,16 @@
+require 'fileutils'
+
+module UploadHelpers
+ extend self
+
+ def uploaded_image_temp_path
+ basename = 'banana_sample.gif'
+ orig_path = File.join(Rails.root, 'spec', 'fixtures', basename)
+ tmp_path = File.join(Rails.root, 'tmp', 'tests', basename)
+ # Because we use 'move_to_store' on all uploaders, we create a new
+ # tempfile on each call: the file we return here will be renamed in most
+ # cases.
+ FileUtils.copy(orig_path, tmp_path)
+ tmp_path
+ end
+end
diff --git a/spec/uploaders/attachment_uploader_spec.rb b/spec/uploaders/attachment_uploader_spec.rb
new file mode 100644
index 00000000000..6098be5cd45
--- /dev/null
+++ b/spec/uploaders/attachment_uploader_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe AttachmentUploader do
+ let(:issue) { build(:issue) }
+ subject { described_class.new(issue) }
+
+ describe '#move_to_cache' do
+ it 'is true' do
+ expect(subject.move_to_cache).to eq(true)
+ end
+ end
+
+ describe '#move_to_store' do
+ it 'is true' do
+ expect(subject.move_to_store).to eq(true)
+ end
+ end
+end
diff --git a/spec/uploaders/avatar_uploader_spec.rb b/spec/uploaders/avatar_uploader_spec.rb
new file mode 100644
index 00000000000..1f0e8732587
--- /dev/null
+++ b/spec/uploaders/avatar_uploader_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe AvatarUploader do
+ let(:user) { build(:user) }
+ subject { described_class.new(user) }
+
+ describe '#move_to_cache' do
+ it 'is true' do
+ expect(subject.move_to_cache).to eq(true)
+ end
+ end
+
+ describe '#move_to_store' do
+ it 'is true' do
+ expect(subject.move_to_store).to eq(true)
+ end
+ end
+end
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index e8300abed5d..bc86adfef68 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -42,4 +42,16 @@ describe FileUploader do
expect(@uploader.image_or_video?).to be false
end
end
+
+ describe '#move_to_cache' do
+ it 'is true' do
+ expect(@uploader.move_to_cache).to eq(true)
+ end
+ end
+
+ describe '#move_to_store' do
+ it 'is true' do
+ expect(@uploader.move_to_store).to eq(true)
+ end
+ end
end
diff --git a/vendor/assets/fonts/KaTeX_AMS-Regular.eot b/vendor/assets/fonts/KaTeX_AMS-Regular.eot
new file mode 100644
index 00000000000..784276a3cbf
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_AMS-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_AMS-Regular.ttf b/vendor/assets/fonts/KaTeX_AMS-Regular.ttf
new file mode 100644
index 00000000000..6f1e0be2028
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_AMS-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_AMS-Regular.woff b/vendor/assets/fonts/KaTeX_AMS-Regular.woff
new file mode 100644
index 00000000000..4dded4733b3
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_AMS-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_AMS-Regular.woff2 b/vendor/assets/fonts/KaTeX_AMS-Regular.woff2
new file mode 100644
index 00000000000..ea81079c4e2
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_AMS-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.eot b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.eot
new file mode 100644
index 00000000000..1a0db0c568e
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.ttf b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.ttf
new file mode 100644
index 00000000000..b94907dad11
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff
new file mode 100644
index 00000000000..799fa8122ca
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff2 b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff2
new file mode 100644
index 00000000000..73bb5422878
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.eot b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.eot
new file mode 100644
index 00000000000..6cc83d0922c
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.ttf b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.ttf
new file mode 100644
index 00000000000..cf51e2021e4
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff
new file mode 100644
index 00000000000..f5e5c623577
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff2 b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff2
new file mode 100644
index 00000000000..dd76d3488d5
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Bold.eot b/vendor/assets/fonts/KaTeX_Fraktur-Bold.eot
new file mode 100644
index 00000000000..1960b106656
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Fraktur-Bold.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Bold.ttf b/vendor/assets/fonts/KaTeX_Fraktur-Bold.ttf
new file mode 100644
index 00000000000..7b0790f1ae8
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Fraktur-Bold.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff b/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff
new file mode 100644
index 00000000000..dc325713291
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff2 b/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff2
new file mode 100644
index 00000000000..fdc429227ad
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Regular.eot b/vendor/assets/fonts/KaTeX_Fraktur-Regular.eot
new file mode 100644
index 00000000000..e4e73796aea
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Fraktur-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Regular.ttf b/vendor/assets/fonts/KaTeX_Fraktur-Regular.ttf
new file mode 100644
index 00000000000..063bc0263eb
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Fraktur-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff b/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff
new file mode 100644
index 00000000000..c4b18d863f3
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff2 b/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff2
new file mode 100644
index 00000000000..4318d938e26
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Bold.eot b/vendor/assets/fonts/KaTeX_Main-Bold.eot
new file mode 100644
index 00000000000..80fbd022363
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Bold.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Bold.ttf b/vendor/assets/fonts/KaTeX_Main-Bold.ttf
new file mode 100644
index 00000000000..8e10722afae
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Bold.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Bold.woff b/vendor/assets/fonts/KaTeX_Main-Bold.woff
new file mode 100644
index 00000000000..43b361a6005
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Bold.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Bold.woff2 b/vendor/assets/fonts/KaTeX_Main-Bold.woff2
new file mode 100644
index 00000000000..af57a96c148
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Bold.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Italic.eot b/vendor/assets/fonts/KaTeX_Main-Italic.eot
new file mode 100644
index 00000000000..fc770166b5e
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Italic.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Italic.ttf b/vendor/assets/fonts/KaTeX_Main-Italic.ttf
new file mode 100644
index 00000000000..d124495d7b6
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Italic.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Italic.woff b/vendor/assets/fonts/KaTeX_Main-Italic.woff
new file mode 100644
index 00000000000..e623236bc44
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Italic.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Italic.woff2 b/vendor/assets/fonts/KaTeX_Main-Italic.woff2
new file mode 100644
index 00000000000..944e9740bdf
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Italic.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Regular.eot b/vendor/assets/fonts/KaTeX_Main-Regular.eot
new file mode 100644
index 00000000000..dc60c090c7a
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Regular.ttf b/vendor/assets/fonts/KaTeX_Main-Regular.ttf
new file mode 100644
index 00000000000..da5797ffcce
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Regular.woff b/vendor/assets/fonts/KaTeX_Main-Regular.woff
new file mode 100644
index 00000000000..37db672e821
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Regular.woff2 b/vendor/assets/fonts/KaTeX_Main-Regular.woff2
new file mode 100644
index 00000000000..48820424893
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Main-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-BoldItalic.eot b/vendor/assets/fonts/KaTeX_Math-BoldItalic.eot
new file mode 100644
index 00000000000..52c8b8c6b40
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-BoldItalic.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-BoldItalic.ttf b/vendor/assets/fonts/KaTeX_Math-BoldItalic.ttf
new file mode 100644
index 00000000000..a8b527c7ef6
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-BoldItalic.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff b/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff
new file mode 100644
index 00000000000..8940e0b5801
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff2 b/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff2
new file mode 100644
index 00000000000..15cf56d3408
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Italic.eot b/vendor/assets/fonts/KaTeX_Math-Italic.eot
new file mode 100644
index 00000000000..64c8992c477
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-Italic.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Italic.ttf b/vendor/assets/fonts/KaTeX_Math-Italic.ttf
new file mode 100644
index 00000000000..06f39d3a299
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-Italic.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Italic.woff b/vendor/assets/fonts/KaTeX_Math-Italic.woff
new file mode 100644
index 00000000000..cf3b4b79e5b
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-Italic.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Italic.woff2 b/vendor/assets/fonts/KaTeX_Math-Italic.woff2
new file mode 100644
index 00000000000..5f8c4bfa455
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-Italic.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Regular.eot b/vendor/assets/fonts/KaTeX_Math-Regular.eot
new file mode 100644
index 00000000000..5521e6a564d
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Regular.ttf b/vendor/assets/fonts/KaTeX_Math-Regular.ttf
new file mode 100644
index 00000000000..73127082370
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Regular.woff b/vendor/assets/fonts/KaTeX_Math-Regular.woff
new file mode 100644
index 00000000000..0e2ebdf18af
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Regular.woff2 b/vendor/assets/fonts/KaTeX_Math-Regular.woff2
new file mode 100644
index 00000000000..ebe3d028a34
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Math-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Bold.eot b/vendor/assets/fonts/KaTeX_SansSerif-Bold.eot
new file mode 100644
index 00000000000..1660e76a2b6
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Bold.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Bold.ttf b/vendor/assets/fonts/KaTeX_SansSerif-Bold.ttf
new file mode 100644
index 00000000000..dbeb7b92ab5
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Bold.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff b/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff
new file mode 100644
index 00000000000..8f144a8bb31
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff2 b/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff2
new file mode 100644
index 00000000000..329e85557fa
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Italic.eot b/vendor/assets/fonts/KaTeX_SansSerif-Italic.eot
new file mode 100644
index 00000000000..289ae3ff8b7
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Italic.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Italic.ttf b/vendor/assets/fonts/KaTeX_SansSerif-Italic.ttf
new file mode 100644
index 00000000000..b3a2f38f224
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Italic.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff b/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff
new file mode 100644
index 00000000000..bddf7ea6579
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff2 b/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff2
new file mode 100644
index 00000000000..5fa767bddd6
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Regular.eot b/vendor/assets/fonts/KaTeX_SansSerif-Regular.eot
new file mode 100644
index 00000000000..1b38b98a180
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Regular.ttf b/vendor/assets/fonts/KaTeX_SansSerif-Regular.ttf
new file mode 100644
index 00000000000..e4712f84775
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff b/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff
new file mode 100644
index 00000000000..33be368048f
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff2 b/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff2
new file mode 100644
index 00000000000..4fcb2e29a05
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Script-Regular.eot b/vendor/assets/fonts/KaTeX_Script-Regular.eot
new file mode 100644
index 00000000000..7870d7f319b
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Script-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Script-Regular.ttf b/vendor/assets/fonts/KaTeX_Script-Regular.ttf
new file mode 100644
index 00000000000..da4d11308ae
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Script-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Script-Regular.woff b/vendor/assets/fonts/KaTeX_Script-Regular.woff
new file mode 100644
index 00000000000..d6ae79f998a
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Script-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Script-Regular.woff2 b/vendor/assets/fonts/KaTeX_Script-Regular.woff2
new file mode 100644
index 00000000000..1b43deb45a8
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Script-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size1-Regular.eot b/vendor/assets/fonts/KaTeX_Size1-Regular.eot
new file mode 100644
index 00000000000..29950f95ff6
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size1-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size1-Regular.ttf b/vendor/assets/fonts/KaTeX_Size1-Regular.ttf
new file mode 100644
index 00000000000..194466a655d
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size1-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size1-Regular.woff b/vendor/assets/fonts/KaTeX_Size1-Regular.woff
new file mode 100644
index 00000000000..237f271edd1
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size1-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size1-Regular.woff2 b/vendor/assets/fonts/KaTeX_Size1-Regular.woff2
new file mode 100644
index 00000000000..39b6f8f746c
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size1-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size2-Regular.eot b/vendor/assets/fonts/KaTeX_Size2-Regular.eot
new file mode 100644
index 00000000000..b8b0536f967
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size2-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size2-Regular.ttf b/vendor/assets/fonts/KaTeX_Size2-Regular.ttf
new file mode 100644
index 00000000000..b41b66a638f
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size2-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size2-Regular.woff b/vendor/assets/fonts/KaTeX_Size2-Regular.woff
new file mode 100644
index 00000000000..4a3055854ed
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size2-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size2-Regular.woff2 b/vendor/assets/fonts/KaTeX_Size2-Regular.woff2
new file mode 100644
index 00000000000..3facec1ab89
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size2-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size3-Regular.eot b/vendor/assets/fonts/KaTeX_Size3-Regular.eot
new file mode 100644
index 00000000000..576b864fae6
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size3-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size3-Regular.ttf b/vendor/assets/fonts/KaTeX_Size3-Regular.ttf
new file mode 100644
index 00000000000..790ddbbc55f
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size3-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size3-Regular.woff b/vendor/assets/fonts/KaTeX_Size3-Regular.woff
new file mode 100644
index 00000000000..3a6d062e660
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size3-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size3-Regular.woff2 b/vendor/assets/fonts/KaTeX_Size3-Regular.woff2
new file mode 100644
index 00000000000..2cffafe5018
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size3-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size4-Regular.eot b/vendor/assets/fonts/KaTeX_Size4-Regular.eot
new file mode 100644
index 00000000000..c2b045fc3db
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size4-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size4-Regular.ttf b/vendor/assets/fonts/KaTeX_Size4-Regular.ttf
new file mode 100644
index 00000000000..ce660aa7ff9
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size4-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size4-Regular.woff b/vendor/assets/fonts/KaTeX_Size4-Regular.woff
new file mode 100644
index 00000000000..7826c6c97a1
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size4-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size4-Regular.woff2 b/vendor/assets/fonts/KaTeX_Size4-Regular.woff2
new file mode 100644
index 00000000000..c92189812d9
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Size4-Regular.woff2
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Typewriter-Regular.eot b/vendor/assets/fonts/KaTeX_Typewriter-Regular.eot
new file mode 100644
index 00000000000..4c178f484a8
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Typewriter-Regular.eot
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Typewriter-Regular.ttf b/vendor/assets/fonts/KaTeX_Typewriter-Regular.ttf
new file mode 100644
index 00000000000..b0427ad0a56
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Typewriter-Regular.ttf
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff b/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff
new file mode 100644
index 00000000000..78e990488a9
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff2 b/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff2
new file mode 100644
index 00000000000..618de99d480
--- /dev/null
+++ b/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff2
Binary files differ
diff --git a/vendor/assets/javascripts/katex.js b/vendor/assets/javascripts/katex.js
new file mode 100644
index 00000000000..9596b839832
--- /dev/null
+++ b/vendor/assets/javascripts/katex.js
@@ -0,0 +1,8642 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.katex = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+/* eslint no-console:0 */
+/**
+ * This is the main entry point for KaTeX. Here, we expose functions for
+ * rendering expressions either to DOM nodes or to markup strings.
+ *
+ * We also expose the ParseError class to check if errors thrown from KaTeX are
+ * errors in the expression, or errors in javascript handling.
+ */
+
+var ParseError = require("./src/ParseError");
+var Settings = require("./src/Settings");
+
+var buildTree = require("./src/buildTree");
+var parseTree = require("./src/parseTree");
+var utils = require("./src/utils");
+
+/**
+ * Parse and build an expression, and place that expression in the DOM node
+ * given.
+ */
+var render = function(expression, baseNode, options) {
+ utils.clearNode(baseNode);
+
+ var settings = new Settings(options);
+
+ var tree = parseTree(expression, settings);
+ var node = buildTree(tree, expression, settings).toNode();
+
+ baseNode.appendChild(node);
+};
+
+// KaTeX's styles don't work properly in quirks mode. Print out an error, and
+// disable rendering.
+if (typeof document !== "undefined") {
+ if (document.compatMode !== "CSS1Compat") {
+ typeof console !== "undefined" && console.warn(
+ "Warning: KaTeX doesn't work in quirks mode. Make sure your " +
+ "website has a suitable doctype.");
+
+ render = function() {
+ throw new ParseError("KaTeX doesn't work in quirks mode.");
+ };
+ }
+}
+
+/**
+ * Parse and build an expression, and return the markup for that.
+ */
+var renderToString = function(expression, options) {
+ var settings = new Settings(options);
+
+ var tree = parseTree(expression, settings);
+ return buildTree(tree, expression, settings).toMarkup();
+};
+
+/**
+ * Parse an expression and return the parse tree.
+ */
+var generateParseTree = function(expression, options) {
+ var settings = new Settings(options);
+ return parseTree(expression, settings);
+};
+
+module.exports = {
+ render: render,
+ renderToString: renderToString,
+ /**
+ * NOTE: This method is not currently recommended for public use.
+ * The internal tree representation is unstable and is very likely
+ * to change. Use at your own risk.
+ */
+ __parse: generateParseTree,
+ ParseError: ParseError,
+};
+
+},{"./src/ParseError":6,"./src/Settings":8,"./src/buildTree":13,"./src/parseTree":22,"./src/utils":25}],2:[function(require,module,exports){
+/** @flow */
+
+"use strict";
+
+function getRelocatable(re) {
+ // In the future, this could use a WeakMap instead of an expando.
+ if (!re.__matchAtRelocatable) {
+ // Disjunctions are the lowest-precedence operator, so we can make any
+ // pattern match the empty string by appending `|()` to it:
+ // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-patterns
+ var source = re.source + "|()";
+
+ // We always make the new regex global.
+ var flags = "g" + (re.ignoreCase ? "i" : "") + (re.multiline ? "m" : "") + (re.unicode ? "u" : "")
+ // sticky (/.../y) doesn't make sense in conjunction with our relocation
+ // logic, so we ignore it here.
+ ;
+
+ re.__matchAtRelocatable = new RegExp(source, flags);
+ }
+ return re.__matchAtRelocatable;
+}
+
+function matchAt(re, str, pos) {
+ if (re.global || re.sticky) {
+ throw new Error("matchAt(...): Only non-global regexes are supported");
+ }
+ var reloc = getRelocatable(re);
+ reloc.lastIndex = pos;
+ var match = reloc.exec(str);
+ // Last capturing group is our sentinel that indicates whether the regex
+ // matched at the given location.
+ if (match[match.length - 1] == null) {
+ // Original regex matched.
+ match.length = match.length - 1;
+ return match;
+ } else {
+ return null;
+ }
+}
+
+module.exports = matchAt;
+},{}],3:[function(require,module,exports){
+/**
+ * The Lexer class handles tokenizing the input in various ways. Since our
+ * parser expects us to be able to backtrack, the lexer allows lexing from any
+ * given starting point.
+ *
+ * Its main exposed function is the `lex` function, which takes a position to
+ * lex from and a type of token to lex. It defers to the appropriate `_innerLex`
+ * function.
+ *
+ * The various `_innerLex` functions perform the actual lexing of different
+ * kinds.
+ */
+
+var matchAt = require("match-at");
+
+var ParseError = require("./ParseError");
+
+// The main lexer class
+function Lexer(input) {
+ this.input = input;
+ this.pos = 0;
+}
+
+/**
+ * The resulting token returned from `lex`.
+ *
+ * It consists of the token text plus some position information.
+ * The position information is essentially a range in an input string,
+ * but instead of referencing the bare input string, we refer to the lexer.
+ * That way it is possible to attach extra metadata to the input string,
+ * like for example a file name or similar.
+ *
+ * The position information (all three parameters) is optional,
+ * so it is OK to construct synthetic tokens if appropriate.
+ * Not providing available position information may lead to
+ * degraded error reporting, though.
+ *
+ * @param {string} text the text of this token
+ * @param {number=} start the start offset, zero-based inclusive
+ * @param {number=} end the end offset, zero-based exclusive
+ * @param {Lexer=} lexer the lexer which in turn holds the input string
+ */
+function Token(text, start, end, lexer) {
+ this.text = text;
+ this.start = start;
+ this.end = end;
+ this.lexer = lexer;
+}
+
+/**
+ * Given a pair of tokens (this and endToken), compute a “Token” encompassing
+ * the whole input range enclosed by these two.
+ *
+ * @param {Token} endToken last token of the range, inclusive
+ * @param {string} text the text of the newly constructed token
+ */
+Token.prototype.range = function(endToken, text) {
+ if (endToken.lexer !== this.lexer) {
+ return new Token(text); // sorry, no position information available
+ }
+ return new Token(text, this.start, endToken.end, this.lexer);
+};
+
+/* The following tokenRegex
+ * - matches typical whitespace (but not NBSP etc.) using its first group
+ * - does not match any control character \x00-\x1f except whitespace
+ * - does not match a bare backslash
+ * - matches any ASCII character except those just mentioned
+ * - does not match the BMP private use area \uE000-\uF8FF
+ * - does not match bare surrogate code units
+ * - matches any BMP character except for those just described
+ * - matches any valid Unicode surrogate pair
+ * - matches a backslash followed by one or more letters
+ * - matches a backslash followed by any BMP character, including newline
+ * Just because the Lexer matches something doesn't mean it's valid input:
+ * If there is no matching function or symbol definition, the Parser will
+ * still reject the input.
+ */
+var tokenRegex = new RegExp(
+ "([ \r\n\t]+)|" + // whitespace
+ "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
+ "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair
+ "|\\\\(?:[a-zA-Z]+|[^\uD800-\uDFFF])" + // function name
+ ")"
+);
+
+/**
+ * This function lexes a single token.
+ */
+Lexer.prototype.lex = function() {
+ var input = this.input;
+ var pos = this.pos;
+ if (pos === input.length) {
+ return new Token("EOF", pos, pos, this);
+ }
+ var match = matchAt(tokenRegex, input, pos);
+ if (match === null) {
+ throw new ParseError(
+ "Unexpected character: '" + input[pos] + "'",
+ new Token(input[pos], pos, pos + 1, this));
+ }
+ var text = match[2] || " ";
+ var start = this.pos;
+ this.pos += match[0].length;
+ var end = this.pos;
+ return new Token(text, start, end, this);
+};
+
+module.exports = Lexer;
+
+},{"./ParseError":6,"match-at":2}],4:[function(require,module,exports){
+/**
+ * This file contains the “gullet” where macros are expanded
+ * until only non-macro tokens remain.
+ */
+
+var Lexer = require("./Lexer");
+
+function MacroExpander(input, macros) {
+ this.lexer = new Lexer(input);
+ this.macros = macros;
+ this.stack = []; // contains tokens in REVERSE order
+ this.discardedWhiteSpace = [];
+}
+
+/**
+ * Recursively expand first token, then return first non-expandable token.
+ */
+MacroExpander.prototype.nextToken = function() {
+ for (;;) {
+ if (this.stack.length === 0) {
+ this.stack.push(this.lexer.lex());
+ }
+ var topToken = this.stack.pop();
+ var name = topToken.text;
+ if (!(name.charAt(0) === "\\" && this.macros.hasOwnProperty(name))) {
+ return topToken;
+ }
+ var expansion = this.macros[name];
+ if (typeof expansion === "string") {
+ var bodyLexer = new Lexer(expansion);
+ expansion = [];
+ var tok = bodyLexer.lex();
+ while (tok.text !== "EOF") {
+ expansion.push(tok);
+ tok = bodyLexer.lex();
+ }
+ expansion.reverse(); // to fit in with stack using push and pop
+ this.macros[name] = expansion;
+ }
+ this.stack = this.stack.concat(expansion);
+ }
+};
+
+MacroExpander.prototype.get = function(ignoreSpace) {
+ this.discardedWhiteSpace = [];
+ var token = this.nextToken();
+ if (ignoreSpace) {
+ while (token.text === " ") {
+ this.discardedWhiteSpace.push(token);
+ token = this.nextToken();
+ }
+ }
+ return token;
+};
+
+/**
+ * Undo the effect of the preceding call to the get method.
+ * A call to this method MUST be immediately preceded and immediately followed
+ * by a call to get. Only used during mode switching, i.e. after one token
+ * was got in the old mode but should get got again in a new mode
+ * with possibly different whitespace handling.
+ */
+MacroExpander.prototype.unget = function(token) {
+ this.stack.push(token);
+ while (this.discardedWhiteSpace.length !== 0) {
+ this.stack.push(this.discardedWhiteSpace.pop());
+ }
+};
+
+module.exports = MacroExpander;
+
+},{"./Lexer":3}],5:[function(require,module,exports){
+/**
+ * This file contains information about the options that the Parser carries
+ * around with it while parsing. Data is held in an `Options` object, and when
+ * recursing, a new `Options` object can be created with the `.with*` and
+ * `.reset` functions.
+ */
+
+/**
+ * This is the main options class. It contains the style, size, color, and font
+ * of the current parse level. It also contains the style and size of the parent
+ * parse level, so size changes can be handled efficiently.
+ *
+ * Each of the `.with*` and `.reset` functions passes its current style and size
+ * as the parentStyle and parentSize of the new options class, so parent
+ * handling is taken care of automatically.
+ */
+function Options(data) {
+ this.style = data.style;
+ this.color = data.color;
+ this.size = data.size;
+ this.phantom = data.phantom;
+ this.font = data.font;
+
+ if (data.parentStyle === undefined) {
+ this.parentStyle = data.style;
+ } else {
+ this.parentStyle = data.parentStyle;
+ }
+
+ if (data.parentSize === undefined) {
+ this.parentSize = data.size;
+ } else {
+ this.parentSize = data.parentSize;
+ }
+}
+
+/**
+ * Returns a new options object with the same properties as "this". Properties
+ * from "extension" will be copied to the new options object.
+ */
+Options.prototype.extend = function(extension) {
+ var data = {
+ style: this.style,
+ size: this.size,
+ color: this.color,
+ parentStyle: this.style,
+ parentSize: this.size,
+ phantom: this.phantom,
+ font: this.font,
+ };
+
+ for (var key in extension) {
+ if (extension.hasOwnProperty(key)) {
+ data[key] = extension[key];
+ }
+ }
+
+ return new Options(data);
+};
+
+/**
+ * Create a new options object with the given style.
+ */
+Options.prototype.withStyle = function(style) {
+ return this.extend({
+ style: style,
+ });
+};
+
+/**
+ * Create a new options object with the given size.
+ */
+Options.prototype.withSize = function(size) {
+ return this.extend({
+ size: size,
+ });
+};
+
+/**
+ * Create a new options object with the given color.
+ */
+Options.prototype.withColor = function(color) {
+ return this.extend({
+ color: color,
+ });
+};
+
+/**
+ * Create a new options object with "phantom" set to true.
+ */
+Options.prototype.withPhantom = function() {
+ return this.extend({
+ phantom: true,
+ });
+};
+
+/**
+ * Create a new options objects with the give font.
+ */
+Options.prototype.withFont = function(font) {
+ return this.extend({
+ font: font,
+ });
+};
+
+/**
+ * Create a new options object with the same style, size, and color. This is
+ * used so that parent style and size changes are handled correctly.
+ */
+Options.prototype.reset = function() {
+ return this.extend({});
+};
+
+/**
+ * A map of color names to CSS colors.
+ * TODO(emily): Remove this when we have real macros
+ */
+var colorMap = {
+ "katex-blue": "#6495ed",
+ "katex-orange": "#ffa500",
+ "katex-pink": "#ff00af",
+ "katex-red": "#df0030",
+ "katex-green": "#28ae7b",
+ "katex-gray": "gray",
+ "katex-purple": "#9d38bd",
+ "katex-blueA": "#ccfaff",
+ "katex-blueB": "#80f6ff",
+ "katex-blueC": "#63d9ea",
+ "katex-blueD": "#11accd",
+ "katex-blueE": "#0c7f99",
+ "katex-tealA": "#94fff5",
+ "katex-tealB": "#26edd5",
+ "katex-tealC": "#01d1c1",
+ "katex-tealD": "#01a995",
+ "katex-tealE": "#208170",
+ "katex-greenA": "#b6ffb0",
+ "katex-greenB": "#8af281",
+ "katex-greenC": "#74cf70",
+ "katex-greenD": "#1fab54",
+ "katex-greenE": "#0d923f",
+ "katex-goldA": "#ffd0a9",
+ "katex-goldB": "#ffbb71",
+ "katex-goldC": "#ff9c39",
+ "katex-goldD": "#e07d10",
+ "katex-goldE": "#a75a05",
+ "katex-redA": "#fca9a9",
+ "katex-redB": "#ff8482",
+ "katex-redC": "#f9685d",
+ "katex-redD": "#e84d39",
+ "katex-redE": "#bc2612",
+ "katex-maroonA": "#ffbde0",
+ "katex-maroonB": "#ff92c6",
+ "katex-maroonC": "#ed5fa6",
+ "katex-maroonD": "#ca337c",
+ "katex-maroonE": "#9e034e",
+ "katex-purpleA": "#ddd7ff",
+ "katex-purpleB": "#c6b9fc",
+ "katex-purpleC": "#aa87ff",
+ "katex-purpleD": "#7854ab",
+ "katex-purpleE": "#543b78",
+ "katex-mintA": "#f5f9e8",
+ "katex-mintB": "#edf2df",
+ "katex-mintC": "#e0e5cc",
+ "katex-grayA": "#f6f7f7",
+ "katex-grayB": "#f0f1f2",
+ "katex-grayC": "#e3e5e6",
+ "katex-grayD": "#d6d8da",
+ "katex-grayE": "#babec2",
+ "katex-grayF": "#888d93",
+ "katex-grayG": "#626569",
+ "katex-grayH": "#3b3e40",
+ "katex-grayI": "#21242c",
+ "katex-kaBlue": "#314453",
+ "katex-kaGreen": "#71B307",
+};
+
+/**
+ * Gets the CSS color of the current options object, accounting for the
+ * `colorMap`.
+ */
+Options.prototype.getColor = function() {
+ if (this.phantom) {
+ return "transparent";
+ } else {
+ return colorMap[this.color] || this.color;
+ }
+};
+
+module.exports = Options;
+
+},{}],6:[function(require,module,exports){
+/**
+ * This is the ParseError class, which is the main error thrown by KaTeX
+ * functions when something has gone wrong. This is used to distinguish internal
+ * errors from errors in the expression that the user provided.
+ *
+ * If possible, a caller should provide a Token or ParseNode with information
+ * about where in the source string the problem occurred.
+ *
+ * @param {string} message The error message
+ * @param {(Token|ParseNode)=} token An object providing position information
+ */
+function ParseError(message, token) {
+ var error = "KaTeX parse error: " + message;
+ var start;
+ var end;
+
+ if (token && token.lexer && token.start <= token.end) {
+ // If we have the input and a position, make the error a bit fancier
+
+ // Get the input
+ var input = token.lexer.input;
+
+ // Prepend some information
+ start = token.start;
+ end = token.end;
+ if (start === input.length) {
+ error += " at end of input: ";
+ } else {
+ error += " at position " + (start + 1) + ": ";
+ }
+
+ // Underline token in question using combining underscores
+ var underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332");
+
+ // Extract some context from the input and add it to the error
+ var left;
+ if (start > 15) {
+ left = "…" + input.slice(start - 15, start);
+ } else {
+ left = input.slice(0, start);
+ }
+ var right;
+ if (end + 15 < input.length) {
+ right = input.slice(end, end + 15) + "…";
+ } else {
+ right = input.slice(end);
+ }
+ error += left + underlined + right;
+ }
+
+ // Some hackery to make ParseError a prototype of Error
+ // See http://stackoverflow.com/a/8460753
+ var self = new Error(error);
+ self.name = "ParseError";
+ self.__proto__ = ParseError.prototype;
+
+ self.position = start;
+ return self;
+}
+
+// More hackery
+ParseError.prototype.__proto__ = Error.prototype;
+
+module.exports = ParseError;
+
+},{}],7:[function(require,module,exports){
+/* eslint no-constant-condition:0 */
+var functions = require("./functions");
+var environments = require("./environments");
+var MacroExpander = require("./MacroExpander");
+var symbols = require("./symbols");
+var utils = require("./utils");
+var cjkRegex = require("./unicodeRegexes").cjkRegex;
+
+var parseData = require("./parseData");
+var ParseError = require("./ParseError");
+
+/**
+ * This file contains the parser used to parse out a TeX expression from the
+ * input. Since TeX isn't context-free, standard parsers don't work particularly
+ * well.
+ *
+ * The strategy of this parser is as such:
+ *
+ * The main functions (the `.parse...` ones) take a position in the current
+ * parse string to parse tokens from. The lexer (found in Lexer.js, stored at
+ * this.lexer) also supports pulling out tokens at arbitrary places. When
+ * individual tokens are needed at a position, the lexer is called to pull out a
+ * token, which is then used.
+ *
+ * The parser has a property called "mode" indicating the mode that
+ * the parser is currently in. Currently it has to be one of "math" or
+ * "text", which denotes whether the current environment is a math-y
+ * one or a text-y one (e.g. inside \text). Currently, this serves to
+ * limit the functions which can be used in text mode.
+ *
+ * The main functions then return an object which contains the useful data that
+ * was parsed at its given point, and a new position at the end of the parsed
+ * data. The main functions can call each other and continue the parsing by
+ * using the returned position as a new starting point.
+ *
+ * There are also extra `.handle...` functions, which pull out some reused
+ * functionality into self-contained functions.
+ *
+ * The earlier functions return ParseNodes.
+ * The later functions (which are called deeper in the parse) sometimes return
+ * ParseFuncOrArgument, which contain a ParseNode as well as some data about
+ * whether the parsed object is a function which is missing some arguments, or a
+ * standalone object which can be used as an argument to another function.
+ */
+
+/**
+ * Main Parser class
+ */
+function Parser(input, settings) {
+ // Create a new macro expander (gullet) and (indirectly via that) also a
+ // new lexer (mouth) for this parser (stomach, in the language of TeX)
+ this.gullet = new MacroExpander(input, settings.macros);
+ // Store the settings for use in parsing
+ this.settings = settings;
+}
+
+var ParseNode = parseData.ParseNode;
+
+/**
+ * An initial function (without its arguments), or an argument to a function.
+ * The `result` argument should be a ParseNode.
+ */
+function ParseFuncOrArgument(result, isFunction, token) {
+ this.result = result;
+ // Is this a function (i.e. is it something defined in functions.js)?
+ this.isFunction = isFunction;
+ this.token = token;
+}
+
+/**
+ * Checks a result to make sure it has the right type, and throws an
+ * appropriate error otherwise.
+ *
+ * @param {boolean=} consume whether to consume the expected token,
+ * defaults to true
+ */
+Parser.prototype.expect = function(text, consume) {
+ if (this.nextToken.text !== text) {
+ throw new ParseError(
+ "Expected '" + text + "', got '" + this.nextToken.text + "'",
+ this.nextToken
+ );
+ }
+ if (consume !== false) {
+ this.consume();
+ }
+};
+
+/**
+ * Considers the current look ahead token as consumed,
+ * and fetches the one after that as the new look ahead.
+ */
+Parser.prototype.consume = function() {
+ this.nextToken = this.gullet.get(this.mode === "math");
+};
+
+Parser.prototype.switchMode = function(newMode) {
+ this.gullet.unget(this.nextToken);
+ this.mode = newMode;
+ this.consume();
+};
+
+/**
+ * Main parsing function, which parses an entire input.
+ *
+ * @return {?Array.<ParseNode>}
+ */
+Parser.prototype.parse = function() {
+ // Try to parse the input
+ this.mode = "math";
+ this.consume();
+ var parse = this.parseInput();
+ return parse;
+};
+
+/**
+ * Parses an entire input tree.
+ */
+Parser.prototype.parseInput = function() {
+ // Parse an expression
+ var expression = this.parseExpression(false);
+ // If we succeeded, make sure there's an EOF at the end
+ this.expect("EOF", false);
+ return expression;
+};
+
+var endOfExpression = ["}", "\\end", "\\right", "&", "\\\\", "\\cr"];
+
+/**
+ * Parses an "expression", which is a list of atoms.
+ *
+ * @param {boolean} breakOnInfix Should the parsing stop when we hit infix
+ * nodes? This happens when functions have higher precendence
+ * than infix nodes in implicit parses.
+ *
+ * @param {?string} breakOnTokenText The text of the token that the expression
+ * should end with, or `null` if something else should end the
+ * expression.
+ *
+ * @return {ParseNode}
+ */
+Parser.prototype.parseExpression = function(breakOnInfix, breakOnTokenText) {
+ var body = [];
+ // Keep adding atoms to the body until we can't parse any more atoms (either
+ // we reached the end, a }, or a \right)
+ while (true) {
+ var lex = this.nextToken;
+ if (endOfExpression.indexOf(lex.text) !== -1) {
+ break;
+ }
+ if (breakOnTokenText && lex.text === breakOnTokenText) {
+ break;
+ }
+ if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) {
+ break;
+ }
+ var atom = this.parseAtom();
+ if (!atom) {
+ if (!this.settings.throwOnError && lex.text[0] === "\\") {
+ var errorNode = this.handleUnsupportedCmd();
+ body.push(errorNode);
+ continue;
+ }
+
+ break;
+ }
+ body.push(atom);
+ }
+ return this.handleInfixNodes(body);
+};
+
+/**
+ * Rewrites infix operators such as \over with corresponding commands such
+ * as \frac.
+ *
+ * There can only be one infix operator per group. If there's more than one
+ * then the expression is ambiguous. This can be resolved by adding {}.
+ *
+ * @returns {Array}
+ */
+Parser.prototype.handleInfixNodes = function(body) {
+ var overIndex = -1;
+ var funcName;
+
+ for (var i = 0; i < body.length; i++) {
+ var node = body[i];
+ if (node.type === "infix") {
+ if (overIndex !== -1) {
+ throw new ParseError(
+ "only one infix operator per group",
+ node.value.token);
+ }
+ overIndex = i;
+ funcName = node.value.replaceWith;
+ }
+ }
+
+ if (overIndex !== -1) {
+ var numerNode;
+ var denomNode;
+
+ var numerBody = body.slice(0, overIndex);
+ var denomBody = body.slice(overIndex + 1);
+
+ if (numerBody.length === 1 && numerBody[0].type === "ordgroup") {
+ numerNode = numerBody[0];
+ } else {
+ numerNode = new ParseNode("ordgroup", numerBody, this.mode);
+ }
+
+ if (denomBody.length === 1 && denomBody[0].type === "ordgroup") {
+ denomNode = denomBody[0];
+ } else {
+ denomNode = new ParseNode("ordgroup", denomBody, this.mode);
+ }
+
+ var value = this.callFunction(
+ funcName, [numerNode, denomNode], null);
+ return [new ParseNode(value.type, value, this.mode)];
+ } else {
+ return body;
+ }
+};
+
+// The greediness of a superscript or subscript
+var SUPSUB_GREEDINESS = 1;
+
+/**
+ * Handle a subscript or superscript with nice errors.
+ */
+Parser.prototype.handleSupSubscript = function(name) {
+ var symbolToken = this.nextToken;
+ var symbol = symbolToken.text;
+ this.consume();
+ var group = this.parseGroup();
+
+ if (!group) {
+ if (!this.settings.throwOnError && this.nextToken.text[0] === "\\") {
+ return this.handleUnsupportedCmd();
+ } else {
+ throw new ParseError(
+ "Expected group after '" + symbol + "'",
+ symbolToken
+ );
+ }
+ } else if (group.isFunction) {
+ // ^ and _ have a greediness, so handle interactions with functions'
+ // greediness
+ var funcGreediness = functions[group.result].greediness;
+ if (funcGreediness > SUPSUB_GREEDINESS) {
+ return this.parseFunction(group);
+ } else {
+ throw new ParseError(
+ "Got function '" + group.result + "' with no arguments " +
+ "as " + name, symbolToken);
+ }
+ } else {
+ return group.result;
+ }
+};
+
+/**
+ * Converts the textual input of an unsupported command into a text node
+ * contained within a color node whose color is determined by errorColor
+ */
+Parser.prototype.handleUnsupportedCmd = function() {
+ var text = this.nextToken.text;
+ var textordArray = [];
+
+ for (var i = 0; i < text.length; i++) {
+ textordArray.push(new ParseNode("textord", text[i], "text"));
+ }
+
+ var textNode = new ParseNode(
+ "text",
+ {
+ body: textordArray,
+ type: "text",
+ },
+ this.mode);
+
+ var colorNode = new ParseNode(
+ "color",
+ {
+ color: this.settings.errorColor,
+ value: [textNode],
+ type: "color",
+ },
+ this.mode);
+
+ this.consume();
+ return colorNode;
+};
+
+/**
+ * Parses a group with optional super/subscripts.
+ *
+ * @return {?ParseNode}
+ */
+Parser.prototype.parseAtom = function() {
+ // The body of an atom is an implicit group, so that things like
+ // \left(x\right)^2 work correctly.
+ var base = this.parseImplicitGroup();
+
+ // In text mode, we don't have superscripts or subscripts
+ if (this.mode === "text") {
+ return base;
+ }
+
+ // Note that base may be empty (i.e. null) at this point.
+
+ var superscript;
+ var subscript;
+ while (true) {
+ // Lex the first token
+ var lex = this.nextToken;
+
+ if (lex.text === "\\limits" || lex.text === "\\nolimits") {
+ // We got a limit control
+ if (!base || base.type !== "op") {
+ throw new ParseError(
+ "Limit controls must follow a math operator",
+ lex);
+ } else {
+ var limits = lex.text === "\\limits";
+ base.value.limits = limits;
+ base.value.alwaysHandleSupSub = true;
+ }
+ this.consume();
+ } else if (lex.text === "^") {
+ // We got a superscript start
+ if (superscript) {
+ throw new ParseError("Double superscript", lex);
+ }
+ superscript = this.handleSupSubscript("superscript");
+ } else if (lex.text === "_") {
+ // We got a subscript start
+ if (subscript) {
+ throw new ParseError("Double subscript", lex);
+ }
+ subscript = this.handleSupSubscript("subscript");
+ } else if (lex.text === "'") {
+ // We got a prime
+ var prime = new ParseNode("textord", "\\prime", this.mode);
+
+ // Many primes can be grouped together, so we handle this here
+ var primes = [prime];
+ this.consume();
+ // Keep lexing tokens until we get something that's not a prime
+ while (this.nextToken.text === "'") {
+ // For each one, add another prime to the list
+ primes.push(prime);
+ this.consume();
+ }
+ // Put them into an ordgroup as the superscript
+ superscript = new ParseNode("ordgroup", primes, this.mode);
+ } else {
+ // If it wasn't ^, _, or ', stop parsing super/subscripts
+ break;
+ }
+ }
+
+ if (superscript || subscript) {
+ // If we got either a superscript or subscript, create a supsub
+ return new ParseNode("supsub", {
+ base: base,
+ sup: superscript,
+ sub: subscript,
+ }, this.mode);
+ } else {
+ // Otherwise return the original body
+ return base;
+ }
+};
+
+// A list of the size-changing functions, for use in parseImplicitGroup
+var sizeFuncs = [
+ "\\tiny", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize",
+ "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge",
+];
+
+// A list of the style-changing functions, for use in parseImplicitGroup
+var styleFuncs = [
+ "\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle",
+];
+
+/**
+ * Parses an implicit group, which is a group that starts at the end of a
+ * specified, and ends right before a higher explicit group ends, or at EOL. It
+ * is used for functions that appear to affect the current style, like \Large or
+ * \textrm, where instead of keeping a style we just pretend that there is an
+ * implicit grouping after it until the end of the group. E.g.
+ * small text {\Large large text} small text again
+ * It is also used for \left and \right to get the correct grouping.
+ *
+ * @return {?ParseNode}
+ */
+Parser.prototype.parseImplicitGroup = function() {
+ var start = this.parseSymbol();
+
+ if (start == null) {
+ // If we didn't get anything we handle, fall back to parseFunction
+ return this.parseFunction();
+ }
+
+ var func = start.result;
+ var body;
+
+ if (func === "\\left") {
+ // If we see a left:
+ // Parse the entire left function (including the delimiter)
+ var left = this.parseFunction(start);
+ // Parse out the implicit body
+ body = this.parseExpression(false);
+ // Check the next token
+ this.expect("\\right", false);
+ var right = this.parseFunction();
+ return new ParseNode("leftright", {
+ body: body,
+ left: left.value.value,
+ right: right.value.value,
+ }, this.mode);
+ } else if (func === "\\begin") {
+ // begin...end is similar to left...right
+ var begin = this.parseFunction(start);
+ var envName = begin.value.name;
+ if (!environments.hasOwnProperty(envName)) {
+ throw new ParseError(
+ "No such environment: " + envName, begin.value.nameGroup);
+ }
+ // Build the environment object. Arguments and other information will
+ // be made available to the begin and end methods using properties.
+ var env = environments[envName];
+ var args = this.parseArguments("\\begin{" + envName + "}", env);
+ var context = {
+ mode: this.mode,
+ envName: envName,
+ parser: this,
+ positions: args.pop(),
+ };
+ var result = env.handler(context, args);
+ this.expect("\\end", false);
+ var endNameToken = this.nextToken;
+ var end = this.parseFunction();
+ if (end.value.name !== envName) {
+ throw new ParseError(
+ "Mismatch: \\begin{" + envName + "} matched " +
+ "by \\end{" + end.value.name + "}",
+ endNameToken);
+ }
+ result.position = end.position;
+ return result;
+ } else if (utils.contains(sizeFuncs, func)) {
+ // If we see a sizing function, parse out the implict body
+ body = this.parseExpression(false);
+ return new ParseNode("sizing", {
+ // Figure out what size to use based on the list of functions above
+ size: "size" + (utils.indexOf(sizeFuncs, func) + 1),
+ value: body,
+ }, this.mode);
+ } else if (utils.contains(styleFuncs, func)) {
+ // If we see a styling function, parse out the implict body
+ body = this.parseExpression(true);
+ return new ParseNode("styling", {
+ // Figure out what style to use by pulling out the style from
+ // the function name
+ style: func.slice(1, func.length - 5),
+ value: body,
+ }, this.mode);
+ } else {
+ // Defer to parseFunction if it's not a function we handle
+ return this.parseFunction(start);
+ }
+};
+
+/**
+ * Parses an entire function, including its base and all of its arguments.
+ * The base might either have been parsed already, in which case
+ * it is provided as an argument, or it's the next group in the input.
+ *
+ * @param {ParseFuncOrArgument=} baseGroup optional as described above
+ * @return {?ParseNode}
+ */
+Parser.prototype.parseFunction = function(baseGroup) {
+ if (!baseGroup) {
+ baseGroup = this.parseGroup();
+ }
+
+ if (baseGroup) {
+ if (baseGroup.isFunction) {
+ var func = baseGroup.result;
+ var funcData = functions[func];
+ if (this.mode === "text" && !funcData.allowedInText) {
+ throw new ParseError(
+ "Can't use function '" + func + "' in text mode",
+ baseGroup.token);
+ }
+
+ var args = this.parseArguments(func, funcData);
+ var token = baseGroup.token;
+ var result = this.callFunction(func, args, args.pop(), token);
+ return new ParseNode(result.type, result, this.mode);
+ } else {
+ return baseGroup.result;
+ }
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Call a function handler with a suitable context and arguments.
+ */
+Parser.prototype.callFunction = function(name, args, positions, token) {
+ var context = {
+ funcName: name,
+ parser: this,
+ positions: positions,
+ token: token,
+ };
+ return functions[name].handler(context, args);
+};
+
+/**
+ * Parses the arguments of a function or environment
+ *
+ * @param {string} func "\name" or "\begin{name}"
+ * @param {{numArgs:number,numOptionalArgs:number|undefined}} funcData
+ * @return the array of arguments, with the list of positions as last element
+ */
+Parser.prototype.parseArguments = function(func, funcData) {
+ var totalArgs = funcData.numArgs + funcData.numOptionalArgs;
+ if (totalArgs === 0) {
+ return [[this.pos]];
+ }
+
+ var baseGreediness = funcData.greediness;
+ var positions = [this.pos];
+ var args = [];
+
+ for (var i = 0; i < totalArgs; i++) {
+ var nextToken = this.nextToken;
+ var argType = funcData.argTypes && funcData.argTypes[i];
+ var arg;
+ if (i < funcData.numOptionalArgs) {
+ if (argType) {
+ arg = this.parseGroupOfType(argType, true);
+ } else {
+ arg = this.parseGroup(true);
+ }
+ if (!arg) {
+ args.push(null);
+ positions.push(this.pos);
+ continue;
+ }
+ } else {
+ if (argType) {
+ arg = this.parseGroupOfType(argType);
+ } else {
+ arg = this.parseGroup();
+ }
+ if (!arg) {
+ if (!this.settings.throwOnError &&
+ this.nextToken.text[0] === "\\") {
+ arg = new ParseFuncOrArgument(
+ this.handleUnsupportedCmd(this.nextToken.text),
+ false);
+ } else {
+ throw new ParseError(
+ "Expected group after '" + func + "'", nextToken);
+ }
+ }
+ }
+ var argNode;
+ if (arg.isFunction) {
+ var argGreediness =
+ functions[arg.result].greediness;
+ if (argGreediness > baseGreediness) {
+ argNode = this.parseFunction(arg);
+ } else {
+ throw new ParseError(
+ "Got function '" + arg.result + "' as " +
+ "argument to '" + func + "'", nextToken);
+ }
+ } else {
+ argNode = arg.result;
+ }
+ args.push(argNode);
+ positions.push(this.pos);
+ }
+
+ args.push(positions);
+
+ return args;
+};
+
+
+/**
+ * Parses a group when the mode is changing.
+ *
+ * @return {?ParseFuncOrArgument}
+ */
+Parser.prototype.parseGroupOfType = function(innerMode, optional) {
+ var outerMode = this.mode;
+ // Handle `original` argTypes
+ if (innerMode === "original") {
+ innerMode = outerMode;
+ }
+
+ if (innerMode === "color") {
+ return this.parseColorGroup(optional);
+ }
+ if (innerMode === "size") {
+ return this.parseSizeGroup(optional);
+ }
+
+ this.switchMode(innerMode);
+ if (innerMode === "text") {
+ // text mode is special because it should ignore the whitespace before
+ // it
+ while (this.nextToken.text === " ") {
+ this.consume();
+ }
+ }
+ // By the time we get here, innerMode is one of "text" or "math".
+ // We switch the mode of the parser, recurse, then restore the old mode.
+ var res = this.parseGroup(optional);
+ this.switchMode(outerMode);
+ return res;
+};
+
+/**
+ * Parses a group, essentially returning the string formed by the
+ * brace-enclosed tokens plus some position information.
+ *
+ * @param {string} modeName Used to describe the mode in error messages
+ * @param {boolean=} optional Whether the group is optional or required
+ */
+Parser.prototype.parseStringGroup = function(modeName, optional) {
+ if (optional && this.nextToken.text !== "[") {
+ return null;
+ }
+ var outerMode = this.mode;
+ this.mode = "text";
+ this.expect(optional ? "[" : "{");
+ var str = "";
+ var firstToken = this.nextToken;
+ var lastToken = firstToken;
+ while (this.nextToken.text !== (optional ? "]" : "}")) {
+ if (this.nextToken.text === "EOF") {
+ throw new ParseError(
+ "Unexpected end of input in " + modeName,
+ firstToken.range(this.nextToken, str));
+ }
+ lastToken = this.nextToken;
+ str += lastToken.text;
+ this.consume();
+ }
+ this.mode = outerMode;
+ this.expect(optional ? "]" : "}");
+ return firstToken.range(lastToken, str);
+};
+
+/**
+ * Parses a color description.
+ */
+Parser.prototype.parseColorGroup = function(optional) {
+ var res = this.parseStringGroup("color", optional);
+ if (!res) {
+ return null;
+ }
+ var match = (/^(#[a-z0-9]+|[a-z]+)$/i).exec(res.text);
+ if (!match) {
+ throw new ParseError("Invalid color: '" + res.text + "'", res);
+ }
+ return new ParseFuncOrArgument(
+ new ParseNode("color", match[0], this.mode),
+ false);
+};
+
+/**
+ * Parses a size specification, consisting of magnitude and unit.
+ */
+Parser.prototype.parseSizeGroup = function(optional) {
+ var res = this.parseStringGroup("size", optional);
+ if (!res) {
+ return null;
+ }
+ var match = (/(-?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/).exec(res.text);
+ if (!match) {
+ throw new ParseError("Invalid size: '" + res.text + "'", res);
+ }
+ var data = {
+ number: +(match[1] + match[2]), // sign + magnitude, cast to number
+ unit: match[3],
+ };
+ if (data.unit !== "em" && data.unit !== "ex") {
+ throw new ParseError("Invalid unit: '" + data.unit + "'", res);
+ }
+ return new ParseFuncOrArgument(
+ new ParseNode("color", data, this.mode),
+ false);
+};
+
+/**
+ * If the argument is false or absent, this parses an ordinary group,
+ * which is either a single nucleus (like "x") or an expression
+ * in braces (like "{x+y}").
+ * If the argument is true, it parses either a bracket-delimited expression
+ * (like "[x+y]") or returns null to indicate the absence of a
+ * bracket-enclosed group.
+ *
+ * @param {boolean=} optional Whether the group is optional or required
+ * @return {?ParseFuncOrArgument}
+ */
+Parser.prototype.parseGroup = function(optional) {
+ var firstToken = this.nextToken;
+ // Try to parse an open brace
+ if (this.nextToken.text === (optional ? "[" : "{")) {
+ // If we get a brace, parse an expression
+ this.consume();
+ var expression = this.parseExpression(false, optional ? "]" : null);
+ var lastToken = this.nextToken;
+ // Make sure we get a close brace
+ this.expect(optional ? "]" : "}");
+ if (this.mode === "text") {
+ this.formLigatures(expression);
+ }
+ return new ParseFuncOrArgument(
+ new ParseNode("ordgroup", expression, this.mode,
+ firstToken, lastToken),
+ false);
+ } else {
+ // Otherwise, just return a nucleus, or nothing for an optional group
+ return optional ? null : this.parseSymbol();
+ }
+};
+
+/**
+ * Form ligature-like combinations of characters for text mode.
+ * This includes inputs like "--", "---", "``" and "''".
+ * The result will simply replace multiple textord nodes with a single
+ * character in each value by a single textord node having multiple
+ * characters in its value. The representation is still ASCII source.
+ *
+ * @param {Array.<ParseNode>} group the nodes of this group,
+ * list will be moified in place
+ */
+Parser.prototype.formLigatures = function(group) {
+ var i;
+ var n = group.length - 1;
+ for (i = 0; i < n; ++i) {
+ var a = group[i];
+ var v = a.value;
+ if (v === "-" && group[i + 1].value === "-") {
+ if (i + 1 < n && group[i + 2].value === "-") {
+ group.splice(i, 3, new ParseNode(
+ "textord", "---", "text", a, group[i + 2]));
+ n -= 2;
+ } else {
+ group.splice(i, 2, new ParseNode(
+ "textord", "--", "text", a, group[i + 1]));
+ n -= 1;
+ }
+ }
+ if ((v === "'" || v === "`") && group[i + 1].value === v) {
+ group.splice(i, 2, new ParseNode(
+ "textord", v + v, "text", a, group[i + 1]));
+ n -= 1;
+ }
+ }
+};
+
+/**
+ * Parse a single symbol out of the string. Here, we handle both the functions
+ * we have defined, as well as the single character symbols
+ *
+ * @return {?ParseFuncOrArgument}
+ */
+Parser.prototype.parseSymbol = function() {
+ var nucleus = this.nextToken;
+
+ if (functions[nucleus.text]) {
+ this.consume();
+ // If there exists a function with this name, we return the function and
+ // say that it is a function.
+ return new ParseFuncOrArgument(
+ nucleus.text,
+ true, nucleus);
+ } else if (symbols[this.mode][nucleus.text]) {
+ this.consume();
+ // Otherwise if this is a no-argument function, find the type it
+ // corresponds to in the symbols map
+ return new ParseFuncOrArgument(
+ new ParseNode(symbols[this.mode][nucleus.text].group,
+ nucleus.text, this.mode, nucleus),
+ false, nucleus);
+ } else if (this.mode === "text" && cjkRegex.test(nucleus.text)) {
+ this.consume();
+ return new ParseFuncOrArgument(
+ new ParseNode("textord", nucleus.text, this.mode, nucleus),
+ false, nucleus);
+ } else {
+ return null;
+ }
+};
+
+Parser.prototype.ParseNode = ParseNode;
+
+module.exports = Parser;
+
+},{"./MacroExpander":4,"./ParseError":6,"./environments":16,"./functions":19,"./parseData":21,"./symbols":23,"./unicodeRegexes":24,"./utils":25}],8:[function(require,module,exports){
+/**
+ * This is a module for storing settings passed into KaTeX. It correctly handles
+ * default settings.
+ */
+
+/**
+ * Helper function for getting a default value if the value is undefined
+ */
+function get(option, defaultValue) {
+ return option === undefined ? defaultValue : option;
+}
+
+/**
+ * The main Settings object
+ *
+ * The current options stored are:
+ * - displayMode: Whether the expression should be typeset by default in
+ * textstyle or displaystyle (default false)
+ */
+function Settings(options) {
+ // allow null options
+ options = options || {};
+ this.displayMode = get(options.displayMode, false);
+ this.throwOnError = get(options.throwOnError, true);
+ this.errorColor = get(options.errorColor, "#cc0000");
+ this.macros = options.macros || {};
+}
+
+module.exports = Settings;
+
+},{}],9:[function(require,module,exports){
+/**
+ * This file contains information and classes for the various kinds of styles
+ * used in TeX. It provides a generic `Style` class, which holds information
+ * about a specific style. It then provides instances of all the different kinds
+ * of styles possible, and provides functions to move between them and get
+ * information about them.
+ */
+
+/**
+ * The main style class. Contains a unique id for the style, a size (which is
+ * the same for cramped and uncramped version of a style), a cramped flag, and a
+ * size multiplier, which gives the size difference between a style and
+ * textstyle.
+ */
+function Style(id, size, multiplier, cramped) {
+ this.id = id;
+ this.size = size;
+ this.cramped = cramped;
+ this.sizeMultiplier = multiplier;
+}
+
+/**
+ * Get the style of a superscript given a base in the current style.
+ */
+Style.prototype.sup = function() {
+ return styles[sup[this.id]];
+};
+
+/**
+ * Get the style of a subscript given a base in the current style.
+ */
+Style.prototype.sub = function() {
+ return styles[sub[this.id]];
+};
+
+/**
+ * Get the style of a fraction numerator given the fraction in the current
+ * style.
+ */
+Style.prototype.fracNum = function() {
+ return styles[fracNum[this.id]];
+};
+
+/**
+ * Get the style of a fraction denominator given the fraction in the current
+ * style.
+ */
+Style.prototype.fracDen = function() {
+ return styles[fracDen[this.id]];
+};
+
+/**
+ * Get the cramped version of a style (in particular, cramping a cramped style
+ * doesn't change the style).
+ */
+Style.prototype.cramp = function() {
+ return styles[cramp[this.id]];
+};
+
+/**
+ * HTML class name, like "displaystyle cramped"
+ */
+Style.prototype.cls = function() {
+ return sizeNames[this.size] + (this.cramped ? " cramped" : " uncramped");
+};
+
+/**
+ * HTML Reset class name, like "reset-textstyle"
+ */
+Style.prototype.reset = function() {
+ return resetNames[this.size];
+};
+
+// IDs of the different styles
+var D = 0;
+var Dc = 1;
+var T = 2;
+var Tc = 3;
+var S = 4;
+var Sc = 5;
+var SS = 6;
+var SSc = 7;
+
+// String names for the different sizes
+var sizeNames = [
+ "displaystyle textstyle",
+ "textstyle",
+ "scriptstyle",
+ "scriptscriptstyle",
+];
+
+// Reset names for the different sizes
+var resetNames = [
+ "reset-textstyle",
+ "reset-textstyle",
+ "reset-scriptstyle",
+ "reset-scriptscriptstyle",
+];
+
+// Instances of the different styles
+var styles = [
+ new Style(D, 0, 1.0, false),
+ new Style(Dc, 0, 1.0, true),
+ new Style(T, 1, 1.0, false),
+ new Style(Tc, 1, 1.0, true),
+ new Style(S, 2, 0.7, false),
+ new Style(Sc, 2, 0.7, true),
+ new Style(SS, 3, 0.5, false),
+ new Style(SSc, 3, 0.5, true),
+];
+
+// Lookup tables for switching from one style to another
+var sup = [S, Sc, S, Sc, SS, SSc, SS, SSc];
+var sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc];
+var fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc];
+var fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc];
+var cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc];
+
+// We only export some of the styles. Also, we don't export the `Style` class so
+// no more styles can be generated.
+module.exports = {
+ DISPLAY: styles[D],
+ TEXT: styles[T],
+ SCRIPT: styles[S],
+ SCRIPTSCRIPT: styles[SS],
+};
+
+},{}],10:[function(require,module,exports){
+/* eslint no-console:0 */
+/**
+ * This module contains general functions that can be used for building
+ * different kinds of domTree nodes in a consistent manner.
+ */
+
+var domTree = require("./domTree");
+var fontMetrics = require("./fontMetrics");
+var symbols = require("./symbols");
+var utils = require("./utils");
+
+var greekCapitals = [
+ "\\Gamma",
+ "\\Delta",
+ "\\Theta",
+ "\\Lambda",
+ "\\Xi",
+ "\\Pi",
+ "\\Sigma",
+ "\\Upsilon",
+ "\\Phi",
+ "\\Psi",
+ "\\Omega",
+];
+
+// The following have to be loaded from Main-Italic font, using class mainit
+var mainitLetters = [
+ "\u0131", // dotless i, \imath
+ "\u0237", // dotless j, \jmath
+ "\u00a3", // \pounds
+];
+
+/**
+ * Makes a symbolNode after translation via the list of symbols in symbols.js.
+ * Correctly pulls out metrics for the character, and optionally takes a list of
+ * classes to be attached to the node.
+ */
+var makeSymbol = function(value, style, mode, color, classes) {
+ // Replace the value with its replaced value from symbol.js
+ if (symbols[mode][value] && symbols[mode][value].replace) {
+ value = symbols[mode][value].replace;
+ }
+
+ var metrics = fontMetrics.getCharacterMetrics(value, style);
+
+ var symbolNode;
+ if (metrics) {
+ symbolNode = new domTree.symbolNode(
+ value, metrics.height, metrics.depth, metrics.italic, metrics.skew,
+ classes);
+ } else {
+ // TODO(emily): Figure out a good way to only print this in development
+ typeof console !== "undefined" && console.warn(
+ "No character metrics for '" + value + "' in style '" +
+ style + "'");
+ symbolNode = new domTree.symbolNode(value, 0, 0, 0, 0, classes);
+ }
+
+ if (color) {
+ symbolNode.style.color = color;
+ }
+
+ return symbolNode;
+};
+
+/**
+ * Makes a symbol in Main-Regular or AMS-Regular.
+ * Used for rel, bin, open, close, inner, and punct.
+ */
+var mathsym = function(value, mode, color, classes) {
+ // Decide what font to render the symbol in by its entry in the symbols
+ // table.
+ // Have a special case for when the value = \ because the \ is used as a
+ // textord in unsupported command errors but cannot be parsed as a regular
+ // text ordinal and is therefore not present as a symbol in the symbols
+ // table for text
+ if (value === "\\" || symbols[mode][value].font === "main") {
+ return makeSymbol(value, "Main-Regular", mode, color, classes);
+ } else {
+ return makeSymbol(
+ value, "AMS-Regular", mode, color, classes.concat(["amsrm"]));
+ }
+};
+
+/**
+ * Makes a symbol in the default font for mathords and textords.
+ */
+var mathDefault = function(value, mode, color, classes, type) {
+ if (type === "mathord") {
+ return mathit(value, mode, color, classes);
+ } else if (type === "textord") {
+ return makeSymbol(
+ value, "Main-Regular", mode, color, classes.concat(["mathrm"]));
+ } else {
+ throw new Error("unexpected type: " + type + " in mathDefault");
+ }
+};
+
+/**
+ * Makes a symbol in the italic math font.
+ */
+var mathit = function(value, mode, color, classes) {
+ if (/[0-9]/.test(value.charAt(0)) ||
+ // glyphs for \imath and \jmath do not exist in Math-Italic so we
+ // need to use Main-Italic instead
+ utils.contains(mainitLetters, value) ||
+ utils.contains(greekCapitals, value)) {
+ return makeSymbol(
+ value, "Main-Italic", mode, color, classes.concat(["mainit"]));
+ } else {
+ return makeSymbol(
+ value, "Math-Italic", mode, color, classes.concat(["mathit"]));
+ }
+};
+
+/**
+ * Makes either a mathord or textord in the correct font and color.
+ */
+var makeOrd = function(group, options, type) {
+ var mode = group.mode;
+ var value = group.value;
+ if (symbols[mode][value] && symbols[mode][value].replace) {
+ value = symbols[mode][value].replace;
+ }
+
+ var classes = ["mord"];
+ var color = options.getColor();
+
+ var font = options.font;
+ if (font) {
+ if (font === "mathit" || utils.contains(mainitLetters, value)) {
+ return mathit(value, mode, color, classes);
+ } else {
+ var fontName = fontMap[font].fontName;
+ if (fontMetrics.getCharacterMetrics(value, fontName)) {
+ return makeSymbol(
+ value, fontName, mode, color, classes.concat([font]));
+ } else {
+ return mathDefault(value, mode, color, classes, type);
+ }
+ }
+ } else {
+ return mathDefault(value, mode, color, classes, type);
+ }
+};
+
+/**
+ * Calculate the height, depth, and maxFontSize of an element based on its
+ * children.
+ */
+var sizeElementFromChildren = function(elem) {
+ var height = 0;
+ var depth = 0;
+ var maxFontSize = 0;
+
+ if (elem.children) {
+ for (var i = 0; i < elem.children.length; i++) {
+ if (elem.children[i].height > height) {
+ height = elem.children[i].height;
+ }
+ if (elem.children[i].depth > depth) {
+ depth = elem.children[i].depth;
+ }
+ if (elem.children[i].maxFontSize > maxFontSize) {
+ maxFontSize = elem.children[i].maxFontSize;
+ }
+ }
+ }
+
+ elem.height = height;
+ elem.depth = depth;
+ elem.maxFontSize = maxFontSize;
+};
+
+/**
+ * Makes a span with the given list of classes, list of children, and color.
+ */
+var makeSpan = function(classes, children, color) {
+ var span = new domTree.span(classes, children);
+
+ sizeElementFromChildren(span);
+
+ if (color) {
+ span.style.color = color;
+ }
+
+ return span;
+};
+
+/**
+ * Makes a document fragment with the given list of children.
+ */
+var makeFragment = function(children) {
+ var fragment = new domTree.documentFragment(children);
+
+ sizeElementFromChildren(fragment);
+
+ return fragment;
+};
+
+/**
+ * Makes an element placed in each of the vlist elements to ensure that each
+ * element has the same max font size. To do this, we create a zero-width space
+ * with the correct font size.
+ */
+var makeFontSizer = function(options, fontSize) {
+ var fontSizeInner = makeSpan([], [new domTree.symbolNode("\u200b")]);
+ fontSizeInner.style.fontSize =
+ (fontSize / options.style.sizeMultiplier) + "em";
+
+ var fontSizer = makeSpan(
+ ["fontsize-ensurer", "reset-" + options.size, "size5"],
+ [fontSizeInner]);
+
+ return fontSizer;
+};
+
+/**
+ * Makes a vertical list by stacking elements and kerns on top of each other.
+ * Allows for many different ways of specifying the positioning method.
+ *
+ * Arguments:
+ * - children: A list of child or kern nodes to be stacked on top of each other
+ * (i.e. the first element will be at the bottom, and the last at
+ * the top). Element nodes are specified as
+ * {type: "elem", elem: node}
+ * while kern nodes are specified as
+ * {type: "kern", size: size}
+ * - positionType: The method by which the vlist should be positioned. Valid
+ * values are:
+ * - "individualShift": The children list only contains elem
+ * nodes, and each node contains an extra
+ * "shift" value of how much it should be
+ * shifted (note that shifting is always
+ * moving downwards). positionData is
+ * ignored.
+ * - "top": The positionData specifies the topmost point of
+ * the vlist (note this is expected to be a height,
+ * so positive values move up)
+ * - "bottom": The positionData specifies the bottommost point
+ * of the vlist (note this is expected to be a
+ * depth, so positive values move down
+ * - "shift": The vlist will be positioned such that its
+ * baseline is positionData away from the baseline
+ * of the first child. Positive values move
+ * downwards.
+ * - "firstBaseline": The vlist will be positioned such that
+ * its baseline is aligned with the
+ * baseline of the first child.
+ * positionData is ignored. (this is
+ * equivalent to "shift" with
+ * positionData=0)
+ * - positionData: Data used in different ways depending on positionType
+ * - options: An Options object
+ *
+ */
+var makeVList = function(children, positionType, positionData, options) {
+ var depth;
+ var currPos;
+ var i;
+ if (positionType === "individualShift") {
+ var oldChildren = children;
+ children = [oldChildren[0]];
+
+ // Add in kerns to the list of children to get each element to be
+ // shifted to the correct specified shift
+ depth = -oldChildren[0].shift - oldChildren[0].elem.depth;
+ currPos = depth;
+ for (i = 1; i < oldChildren.length; i++) {
+ var diff = -oldChildren[i].shift - currPos -
+ oldChildren[i].elem.depth;
+ var size = diff -
+ (oldChildren[i - 1].elem.height +
+ oldChildren[i - 1].elem.depth);
+
+ currPos = currPos + diff;
+
+ children.push({type: "kern", size: size});
+ children.push(oldChildren[i]);
+ }
+ } else if (positionType === "top") {
+ // We always start at the bottom, so calculate the bottom by adding up
+ // all the sizes
+ var bottom = positionData;
+ for (i = 0; i < children.length; i++) {
+ if (children[i].type === "kern") {
+ bottom -= children[i].size;
+ } else {
+ bottom -= children[i].elem.height + children[i].elem.depth;
+ }
+ }
+ depth = bottom;
+ } else if (positionType === "bottom") {
+ depth = -positionData;
+ } else if (positionType === "shift") {
+ depth = -children[0].elem.depth - positionData;
+ } else if (positionType === "firstBaseline") {
+ depth = -children[0].elem.depth;
+ } else {
+ depth = 0;
+ }
+
+ // Make the fontSizer
+ var maxFontSize = 0;
+ for (i = 0; i < children.length; i++) {
+ if (children[i].type === "elem") {
+ maxFontSize = Math.max(maxFontSize, children[i].elem.maxFontSize);
+ }
+ }
+ var fontSizer = makeFontSizer(options, maxFontSize);
+
+ // Create a new list of actual children at the correct offsets
+ var realChildren = [];
+ currPos = depth;
+ for (i = 0; i < children.length; i++) {
+ if (children[i].type === "kern") {
+ currPos += children[i].size;
+ } else {
+ var child = children[i].elem;
+
+ var shift = -child.depth - currPos;
+ currPos += child.height + child.depth;
+
+ var childWrap = makeSpan([], [fontSizer, child]);
+ childWrap.height -= shift;
+ childWrap.depth += shift;
+ childWrap.style.top = shift + "em";
+
+ realChildren.push(childWrap);
+ }
+ }
+
+ // Add in an element at the end with no offset to fix the calculation of
+ // baselines in some browsers (namely IE, sometimes safari)
+ var baselineFix = makeSpan(
+ ["baseline-fix"], [fontSizer, new domTree.symbolNode("\u200b")]);
+ realChildren.push(baselineFix);
+
+ var vlist = makeSpan(["vlist"], realChildren);
+ // Fix the final height and depth, in case there were kerns at the ends
+ // since the makeSpan calculation won't take that in to account.
+ vlist.height = Math.max(currPos, vlist.height);
+ vlist.depth = Math.max(-depth, vlist.depth);
+ return vlist;
+};
+
+// A table of size -> font size for the different sizing functions
+var sizingMultiplier = {
+ size1: 0.5,
+ size2: 0.7,
+ size3: 0.8,
+ size4: 0.9,
+ size5: 1.0,
+ size6: 1.2,
+ size7: 1.44,
+ size8: 1.73,
+ size9: 2.07,
+ size10: 2.49,
+};
+
+// A map of spacing functions to their attributes, like size and corresponding
+// CSS class
+var spacingFunctions = {
+ "\\qquad": {
+ size: "2em",
+ className: "qquad",
+ },
+ "\\quad": {
+ size: "1em",
+ className: "quad",
+ },
+ "\\enspace": {
+ size: "0.5em",
+ className: "enspace",
+ },
+ "\\;": {
+ size: "0.277778em",
+ className: "thickspace",
+ },
+ "\\:": {
+ size: "0.22222em",
+ className: "mediumspace",
+ },
+ "\\,": {
+ size: "0.16667em",
+ className: "thinspace",
+ },
+ "\\!": {
+ size: "-0.16667em",
+ className: "negativethinspace",
+ },
+};
+
+/**
+ * Maps TeX font commands to objects containing:
+ * - variant: string used for "mathvariant" attribute in buildMathML.js
+ * - fontName: the "style" parameter to fontMetrics.getCharacterMetrics
+ */
+// A map between tex font commands an MathML mathvariant attribute values
+var fontMap = {
+ // styles
+ "mathbf": {
+ variant: "bold",
+ fontName: "Main-Bold",
+ },
+ "mathrm": {
+ variant: "normal",
+ fontName: "Main-Regular",
+ },
+
+ // "mathit" is missing because it requires the use of two fonts: Main-Italic
+ // and Math-Italic. This is handled by a special case in makeOrd which ends
+ // up calling mathit.
+
+ // families
+ "mathbb": {
+ variant: "double-struck",
+ fontName: "AMS-Regular",
+ },
+ "mathcal": {
+ variant: "script",
+ fontName: "Caligraphic-Regular",
+ },
+ "mathfrak": {
+ variant: "fraktur",
+ fontName: "Fraktur-Regular",
+ },
+ "mathscr": {
+ variant: "script",
+ fontName: "Script-Regular",
+ },
+ "mathsf": {
+ variant: "sans-serif",
+ fontName: "SansSerif-Regular",
+ },
+ "mathtt": {
+ variant: "monospace",
+ fontName: "Typewriter-Regular",
+ },
+};
+
+module.exports = {
+ fontMap: fontMap,
+ makeSymbol: makeSymbol,
+ mathsym: mathsym,
+ makeSpan: makeSpan,
+ makeFragment: makeFragment,
+ makeVList: makeVList,
+ makeOrd: makeOrd,
+ sizingMultiplier: sizingMultiplier,
+ spacingFunctions: spacingFunctions,
+};
+
+},{"./domTree":15,"./fontMetrics":17,"./symbols":23,"./utils":25}],11:[function(require,module,exports){
+/* eslint no-console:0 */
+/**
+ * This file does the main work of building a domTree structure from a parse
+ * tree. The entry point is the `buildHTML` function, which takes a parse tree.
+ * Then, the buildExpression, buildGroup, and various groupTypes functions are
+ * called, to produce a final HTML tree.
+ */
+
+var ParseError = require("./ParseError");
+var Style = require("./Style");
+
+var buildCommon = require("./buildCommon");
+var delimiter = require("./delimiter");
+var domTree = require("./domTree");
+var fontMetrics = require("./fontMetrics");
+var utils = require("./utils");
+
+var makeSpan = buildCommon.makeSpan;
+
+/**
+ * Take a list of nodes, build them in order, and return a list of the built
+ * nodes. This function handles the `prev` node correctly, and passes the
+ * previous element from the list as the prev of the next element.
+ */
+var buildExpression = function(expression, options, prev) {
+ var groups = [];
+ for (var i = 0; i < expression.length; i++) {
+ var group = expression[i];
+ groups.push(buildGroup(group, options, prev));
+ prev = group;
+ }
+ return groups;
+};
+
+// List of types used by getTypeOfGroup,
+// see https://github.com/Khan/KaTeX/wiki/Examining-TeX#group-types
+var groupToType = {
+ mathord: "mord",
+ textord: "mord",
+ bin: "mbin",
+ rel: "mrel",
+ text: "mord",
+ open: "mopen",
+ close: "mclose",
+ inner: "minner",
+ genfrac: "mord",
+ array: "mord",
+ spacing: "mord",
+ punct: "mpunct",
+ ordgroup: "mord",
+ op: "mop",
+ katex: "mord",
+ overline: "mord",
+ underline: "mord",
+ rule: "mord",
+ leftright: "minner",
+ sqrt: "mord",
+ accent: "mord",
+};
+
+/**
+ * Gets the final math type of an expression, given its group type. This type is
+ * used to determine spacing between elements, and affects bin elements by
+ * causing them to change depending on what types are around them. This type
+ * must be attached to the outermost node of an element as a CSS class so that
+ * spacing with its surrounding elements works correctly.
+ *
+ * Some elements can be mapped one-to-one from group type to math type, and
+ * those are listed in the `groupToType` table.
+ *
+ * Others (usually elements that wrap around other elements) often have
+ * recursive definitions, and thus call `getTypeOfGroup` on their inner
+ * elements.
+ */
+var getTypeOfGroup = function(group) {
+ if (group == null) {
+ // Like when typesetting $^3$
+ return groupToType.mathord;
+ } else if (group.type === "supsub") {
+ return getTypeOfGroup(group.value.base);
+ } else if (group.type === "llap" || group.type === "rlap") {
+ return getTypeOfGroup(group.value);
+ } else if (group.type === "color") {
+ return getTypeOfGroup(group.value.value);
+ } else if (group.type === "sizing") {
+ return getTypeOfGroup(group.value.value);
+ } else if (group.type === "styling") {
+ return getTypeOfGroup(group.value.value);
+ } else if (group.type === "delimsizing") {
+ return groupToType[group.value.delimType];
+ } else {
+ return groupToType[group.type];
+ }
+};
+
+/**
+ * Sometimes, groups perform special rules when they have superscripts or
+ * subscripts attached to them. This function lets the `supsub` group know that
+ * its inner element should handle the superscripts and subscripts instead of
+ * handling them itself.
+ */
+var shouldHandleSupSub = function(group, options) {
+ if (!group) {
+ return false;
+ } else if (group.type === "op") {
+ // Operators handle supsubs differently when they have limits
+ // (e.g. `\displaystyle\sum_2^3`)
+ return group.value.limits &&
+ (options.style.size === Style.DISPLAY.size ||
+ group.value.alwaysHandleSupSub);
+ } else if (group.type === "accent") {
+ return isCharacterBox(group.value.base);
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Sometimes we want to pull out the innermost element of a group. In most
+ * cases, this will just be the group itself, but when ordgroups and colors have
+ * a single element, we want to pull that out.
+ */
+var getBaseElem = function(group) {
+ if (!group) {
+ return false;
+ } else if (group.type === "ordgroup") {
+ if (group.value.length === 1) {
+ return getBaseElem(group.value[0]);
+ } else {
+ return group;
+ }
+ } else if (group.type === "color") {
+ if (group.value.value.length === 1) {
+ return getBaseElem(group.value.value[0]);
+ } else {
+ return group;
+ }
+ } else if (group.type === "font") {
+ return getBaseElem(group.value.body);
+ } else {
+ return group;
+ }
+};
+
+/**
+ * TeXbook algorithms often reference "character boxes", which are simply groups
+ * with a single character in them. To decide if something is a character box,
+ * we find its innermost group, and see if it is a single character.
+ */
+var isCharacterBox = function(group) {
+ var baseElem = getBaseElem(group);
+
+ // These are all they types of groups which hold single characters
+ return baseElem.type === "mathord" ||
+ baseElem.type === "textord" ||
+ baseElem.type === "bin" ||
+ baseElem.type === "rel" ||
+ baseElem.type === "inner" ||
+ baseElem.type === "open" ||
+ baseElem.type === "close" ||
+ baseElem.type === "punct";
+};
+
+var makeNullDelimiter = function(options) {
+ return makeSpan([
+ "sizing", "reset-" + options.size, "size5",
+ options.style.reset(), Style.TEXT.cls(),
+ "nulldelimiter",
+ ]);
+};
+
+/**
+ * This is a map of group types to the function used to handle that type.
+ * Simpler types come at the beginning, while complicated types come afterwards.
+ */
+var groupTypes = {};
+
+groupTypes.mathord = function(group, options, prev) {
+ return buildCommon.makeOrd(group, options, "mathord");
+};
+
+groupTypes.textord = function(group, options, prev) {
+ return buildCommon.makeOrd(group, options, "textord");
+};
+
+groupTypes.bin = function(group, options, prev) {
+ var className = "mbin";
+ // Pull out the most recent element. Do some special handling to find
+ // things at the end of a \color group. Note that we don't use the same
+ // logic for ordgroups (which count as ords).
+ var prevAtom = prev;
+ while (prevAtom && prevAtom.type === "color") {
+ var atoms = prevAtom.value.value;
+ prevAtom = atoms[atoms.length - 1];
+ }
+ // See TeXbook pg. 442-446, Rules 5 and 6, and the text before Rule 19.
+ // Here, we determine whether the bin should turn into an ord. We
+ // currently only apply Rule 5.
+ if (!prev || utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
+ getTypeOfGroup(prevAtom))) {
+ group.type = "textord";
+ className = "mord";
+ }
+
+ return buildCommon.mathsym(
+ group.value, group.mode, options.getColor(), [className]);
+};
+
+groupTypes.rel = function(group, options, prev) {
+ return buildCommon.mathsym(
+ group.value, group.mode, options.getColor(), ["mrel"]);
+};
+
+groupTypes.open = function(group, options, prev) {
+ return buildCommon.mathsym(
+ group.value, group.mode, options.getColor(), ["mopen"]);
+};
+
+groupTypes.close = function(group, options, prev) {
+ return buildCommon.mathsym(
+ group.value, group.mode, options.getColor(), ["mclose"]);
+};
+
+groupTypes.inner = function(group, options, prev) {
+ return buildCommon.mathsym(
+ group.value, group.mode, options.getColor(), ["minner"]);
+};
+
+groupTypes.punct = function(group, options, prev) {
+ return buildCommon.mathsym(
+ group.value, group.mode, options.getColor(), ["mpunct"]);
+};
+
+groupTypes.ordgroup = function(group, options, prev) {
+ return makeSpan(
+ ["mord", options.style.cls()],
+ buildExpression(group.value, options.reset())
+ );
+};
+
+groupTypes.text = function(group, options, prev) {
+ return makeSpan(["text", "mord", options.style.cls()],
+ buildExpression(group.value.body, options.reset()));
+};
+
+groupTypes.color = function(group, options, prev) {
+ var elements = buildExpression(
+ group.value.value,
+ options.withColor(group.value.color),
+ prev
+ );
+
+ // \color isn't supposed to affect the type of the elements it contains.
+ // To accomplish this, we wrap the results in a fragment, so the inner
+ // elements will be able to directly interact with their neighbors. For
+ // example, `\color{red}{2 +} 3` has the same spacing as `2 + 3`
+ return new buildCommon.makeFragment(elements);
+};
+
+groupTypes.supsub = function(group, options, prev) {
+ // Superscript and subscripts are handled in the TeXbook on page
+ // 445-446, rules 18(a-f).
+
+ // Here is where we defer to the inner group if it should handle
+ // superscripts and subscripts itself.
+ if (shouldHandleSupSub(group.value.base, options)) {
+ return groupTypes[group.value.base.type](group, options, prev);
+ }
+
+ var base = buildGroup(group.value.base, options.reset());
+ var supmid;
+ var submid;
+ var sup;
+ var sub;
+
+ if (group.value.sup) {
+ sup = buildGroup(group.value.sup,
+ options.withStyle(options.style.sup()));
+ supmid = makeSpan(
+ [options.style.reset(), options.style.sup().cls()], [sup]);
+ }
+
+ if (group.value.sub) {
+ sub = buildGroup(group.value.sub,
+ options.withStyle(options.style.sub()));
+ submid = makeSpan(
+ [options.style.reset(), options.style.sub().cls()], [sub]);
+ }
+
+ // Rule 18a
+ var supShift;
+ var subShift;
+ if (isCharacterBox(group.value.base)) {
+ supShift = 0;
+ subShift = 0;
+ } else {
+ supShift = base.height - fontMetrics.metrics.supDrop;
+ subShift = base.depth + fontMetrics.metrics.subDrop;
+ }
+
+ // Rule 18c
+ var minSupShift;
+ if (options.style === Style.DISPLAY) {
+ minSupShift = fontMetrics.metrics.sup1;
+ } else if (options.style.cramped) {
+ minSupShift = fontMetrics.metrics.sup3;
+ } else {
+ minSupShift = fontMetrics.metrics.sup2;
+ }
+
+ // scriptspace is a font-size-independent size, so scale it
+ // appropriately
+ var multiplier = Style.TEXT.sizeMultiplier *
+ options.style.sizeMultiplier;
+ var scriptspace =
+ (0.5 / fontMetrics.metrics.ptPerEm) / multiplier + "em";
+
+ var supsub;
+ if (!group.value.sup) {
+ // Rule 18b
+ subShift = Math.max(
+ subShift, fontMetrics.metrics.sub1,
+ sub.height - 0.8 * fontMetrics.metrics.xHeight);
+
+ supsub = buildCommon.makeVList([
+ {type: "elem", elem: submid},
+ ], "shift", subShift, options);
+
+ supsub.children[0].style.marginRight = scriptspace;
+
+ // Subscripts shouldn't be shifted by the base's italic correction.
+ // Account for that by shifting the subscript back the appropriate
+ // amount. Note we only do this when the base is a single symbol.
+ if (base instanceof domTree.symbolNode) {
+ supsub.children[0].style.marginLeft = -base.italic + "em";
+ }
+ } else if (!group.value.sub) {
+ // Rule 18c, d
+ supShift = Math.max(supShift, minSupShift,
+ sup.depth + 0.25 * fontMetrics.metrics.xHeight);
+
+ supsub = buildCommon.makeVList([
+ {type: "elem", elem: supmid},
+ ], "shift", -supShift, options);
+
+ supsub.children[0].style.marginRight = scriptspace;
+ } else {
+ supShift = Math.max(
+ supShift, minSupShift,
+ sup.depth + 0.25 * fontMetrics.metrics.xHeight);
+ subShift = Math.max(subShift, fontMetrics.metrics.sub2);
+
+ var ruleWidth = fontMetrics.metrics.defaultRuleThickness;
+
+ // Rule 18e
+ if ((supShift - sup.depth) - (sub.height - subShift) <
+ 4 * ruleWidth) {
+ subShift = 4 * ruleWidth - (supShift - sup.depth) + sub.height;
+ var psi = 0.8 * fontMetrics.metrics.xHeight -
+ (supShift - sup.depth);
+ if (psi > 0) {
+ supShift += psi;
+ subShift -= psi;
+ }
+ }
+
+ supsub = buildCommon.makeVList([
+ {type: "elem", elem: submid, shift: subShift},
+ {type: "elem", elem: supmid, shift: -supShift},
+ ], "individualShift", null, options);
+
+ // See comment above about subscripts not being shifted
+ if (base instanceof domTree.symbolNode) {
+ supsub.children[0].style.marginLeft = -base.italic + "em";
+ }
+
+ supsub.children[0].style.marginRight = scriptspace;
+ supsub.children[1].style.marginRight = scriptspace;
+ }
+
+ // We ensure to wrap the supsub vlist in a span.msupsub to reset text-align
+ return makeSpan([getTypeOfGroup(group.value.base)],
+ [base, makeSpan(["msupsub"], [supsub])]);
+};
+
+groupTypes.genfrac = function(group, options, prev) {
+ // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
+ // Figure out what style this fraction should be in based on the
+ // function used
+ var fstyle = options.style;
+ if (group.value.size === "display") {
+ fstyle = Style.DISPLAY;
+ } else if (group.value.size === "text") {
+ fstyle = Style.TEXT;
+ }
+
+ var nstyle = fstyle.fracNum();
+ var dstyle = fstyle.fracDen();
+
+ var numer = buildGroup(group.value.numer, options.withStyle(nstyle));
+ var numerreset = makeSpan([fstyle.reset(), nstyle.cls()], [numer]);
+
+ var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
+ var denomreset = makeSpan([fstyle.reset(), dstyle.cls()], [denom]);
+
+ var ruleWidth;
+ if (group.value.hasBarLine) {
+ ruleWidth = fontMetrics.metrics.defaultRuleThickness /
+ options.style.sizeMultiplier;
+ } else {
+ ruleWidth = 0;
+ }
+
+ // Rule 15b
+ var numShift;
+ var clearance;
+ var denomShift;
+ if (fstyle.size === Style.DISPLAY.size) {
+ numShift = fontMetrics.metrics.num1;
+ if (ruleWidth > 0) {
+ clearance = 3 * ruleWidth;
+ } else {
+ clearance = 7 * fontMetrics.metrics.defaultRuleThickness;
+ }
+ denomShift = fontMetrics.metrics.denom1;
+ } else {
+ if (ruleWidth > 0) {
+ numShift = fontMetrics.metrics.num2;
+ clearance = ruleWidth;
+ } else {
+ numShift = fontMetrics.metrics.num3;
+ clearance = 3 * fontMetrics.metrics.defaultRuleThickness;
+ }
+ denomShift = fontMetrics.metrics.denom2;
+ }
+
+ var frac;
+ if (ruleWidth === 0) {
+ // Rule 15c
+ var candiateClearance =
+ (numShift - numer.depth) - (denom.height - denomShift);
+ if (candiateClearance < clearance) {
+ numShift += 0.5 * (clearance - candiateClearance);
+ denomShift += 0.5 * (clearance - candiateClearance);
+ }
+
+ frac = buildCommon.makeVList([
+ {type: "elem", elem: denomreset, shift: denomShift},
+ {type: "elem", elem: numerreset, shift: -numShift},
+ ], "individualShift", null, options);
+ } else {
+ // Rule 15d
+ var axisHeight = fontMetrics.metrics.axisHeight;
+
+ if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth) <
+ clearance) {
+ numShift +=
+ clearance - ((numShift - numer.depth) -
+ (axisHeight + 0.5 * ruleWidth));
+ }
+
+ if ((axisHeight - 0.5 * ruleWidth) - (denom.height - denomShift) <
+ clearance) {
+ denomShift +=
+ clearance - ((axisHeight - 0.5 * ruleWidth) -
+ (denom.height - denomShift));
+ }
+
+ var mid = makeSpan(
+ [options.style.reset(), Style.TEXT.cls(), "frac-line"]);
+ // Manually set the height of the line because its height is
+ // created in CSS
+ mid.height = ruleWidth;
+
+ var midShift = -(axisHeight - 0.5 * ruleWidth);
+
+ frac = buildCommon.makeVList([
+ {type: "elem", elem: denomreset, shift: denomShift},
+ {type: "elem", elem: mid, shift: midShift},
+ {type: "elem", elem: numerreset, shift: -numShift},
+ ], "individualShift", null, options);
+ }
+
+ // Since we manually change the style sometimes (with \dfrac or \tfrac),
+ // account for the possible size change here.
+ frac.height *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
+ frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
+
+ // Rule 15e
+ var delimSize;
+ if (fstyle.size === Style.DISPLAY.size) {
+ delimSize = fontMetrics.metrics.delim1;
+ } else {
+ delimSize = fontMetrics.metrics.getDelim2(fstyle);
+ }
+
+ var leftDelim;
+ var rightDelim;
+ if (group.value.leftDelim == null) {
+ leftDelim = makeNullDelimiter(options);
+ } else {
+ leftDelim = delimiter.customSizedDelim(
+ group.value.leftDelim, delimSize, true,
+ options.withStyle(fstyle), group.mode);
+ }
+ if (group.value.rightDelim == null) {
+ rightDelim = makeNullDelimiter(options);
+ } else {
+ rightDelim = delimiter.customSizedDelim(
+ group.value.rightDelim, delimSize, true,
+ options.withStyle(fstyle), group.mode);
+ }
+
+ return makeSpan(
+ ["mord", options.style.reset(), fstyle.cls()],
+ [leftDelim, makeSpan(["mfrac"], [frac]), rightDelim],
+ options.getColor());
+};
+
+groupTypes.array = function(group, options, prev) {
+ var r;
+ var c;
+ var nr = group.value.body.length;
+ var nc = 0;
+ var body = new Array(nr);
+
+ // Horizontal spacing
+ var pt = 1 / fontMetrics.metrics.ptPerEm;
+ var arraycolsep = 5 * pt; // \arraycolsep in article.cls
+
+ // Vertical spacing
+ var baselineskip = 12 * pt; // see size10.clo
+ // Default \arraystretch from lttab.dtx
+ // TODO(gagern): may get redefined once we have user-defined macros
+ var arraystretch = utils.deflt(group.value.arraystretch, 1);
+ var arrayskip = arraystretch * baselineskip;
+ var arstrutHeight = 0.7 * arrayskip; // \strutbox in ltfsstrc.dtx and
+ var arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx
+
+ var totalHeight = 0;
+ for (r = 0; r < group.value.body.length; ++r) {
+ var inrow = group.value.body[r];
+ var height = arstrutHeight; // \@array adds an \@arstrut
+ var depth = arstrutDepth; // to each tow (via the template)
+
+ if (nc < inrow.length) {
+ nc = inrow.length;
+ }
+
+ var outrow = new Array(inrow.length);
+ for (c = 0; c < inrow.length; ++c) {
+ var elt = buildGroup(inrow[c], options);
+ if (depth < elt.depth) {
+ depth = elt.depth;
+ }
+ if (height < elt.height) {
+ height = elt.height;
+ }
+ outrow[c] = elt;
+ }
+
+ var gap = 0;
+ if (group.value.rowGaps[r]) {
+ gap = group.value.rowGaps[r].value;
+ switch (gap.unit) {
+ case "em":
+ gap = gap.number;
+ break;
+ case "ex":
+ gap = gap.number * fontMetrics.metrics.emPerEx;
+ break;
+ default:
+ console.error("Can't handle unit " + gap.unit);
+ gap = 0;
+ }
+ if (gap > 0) { // \@argarraycr
+ gap += arstrutDepth;
+ if (depth < gap) {
+ depth = gap; // \@xargarraycr
+ }
+ gap = 0;
+ }
+ }
+
+ outrow.height = height;
+ outrow.depth = depth;
+ totalHeight += height;
+ outrow.pos = totalHeight;
+ totalHeight += depth + gap; // \@yargarraycr
+ body[r] = outrow;
+ }
+
+ var offset = totalHeight / 2 + fontMetrics.metrics.axisHeight;
+ var colDescriptions = group.value.cols || [];
+ var cols = [];
+ var colSep;
+ var colDescrNum;
+ for (c = 0, colDescrNum = 0;
+ // Continue while either there are more columns or more column
+ // descriptions, so trailing separators don't get lost.
+ c < nc || colDescrNum < colDescriptions.length;
+ ++c, ++colDescrNum) {
+
+ var colDescr = colDescriptions[colDescrNum] || {};
+
+ var firstSeparator = true;
+ while (colDescr.type === "separator") {
+ // If there is more than one separator in a row, add a space
+ // between them.
+ if (!firstSeparator) {
+ colSep = makeSpan(["arraycolsep"], []);
+ colSep.style.width =
+ fontMetrics.metrics.doubleRuleSep + "em";
+ cols.push(colSep);
+ }
+
+ if (colDescr.separator === "|") {
+ var separator = makeSpan(
+ ["vertical-separator"],
+ []);
+ separator.style.height = totalHeight + "em";
+ separator.style.verticalAlign =
+ -(totalHeight - offset) + "em";
+
+ cols.push(separator);
+ } else {
+ throw new ParseError(
+ "Invalid separator type: " + colDescr.separator);
+ }
+
+ colDescrNum++;
+ colDescr = colDescriptions[colDescrNum] || {};
+ firstSeparator = false;
+ }
+
+ if (c >= nc) {
+ continue;
+ }
+
+ var sepwidth;
+ if (c > 0 || group.value.hskipBeforeAndAfter) {
+ sepwidth = utils.deflt(colDescr.pregap, arraycolsep);
+ if (sepwidth !== 0) {
+ colSep = makeSpan(["arraycolsep"], []);
+ colSep.style.width = sepwidth + "em";
+ cols.push(colSep);
+ }
+ }
+
+ var col = [];
+ for (r = 0; r < nr; ++r) {
+ var row = body[r];
+ var elem = row[c];
+ if (!elem) {
+ continue;
+ }
+ var shift = row.pos - offset;
+ elem.depth = row.depth;
+ elem.height = row.height;
+ col.push({type: "elem", elem: elem, shift: shift});
+ }
+
+ col = buildCommon.makeVList(col, "individualShift", null, options);
+ col = makeSpan(
+ ["col-align-" + (colDescr.align || "c")],
+ [col]);
+ cols.push(col);
+
+ if (c < nc - 1 || group.value.hskipBeforeAndAfter) {
+ sepwidth = utils.deflt(colDescr.postgap, arraycolsep);
+ if (sepwidth !== 0) {
+ colSep = makeSpan(["arraycolsep"], []);
+ colSep.style.width = sepwidth + "em";
+ cols.push(colSep);
+ }
+ }
+ }
+ body = makeSpan(["mtable"], cols);
+ return makeSpan(["mord"], [body], options.getColor());
+};
+
+groupTypes.spacing = function(group, options, prev) {
+ if (group.value === "\\ " || group.value === "\\space" ||
+ group.value === " " || group.value === "~") {
+ // Spaces are generated by adding an actual space. Each of these
+ // things has an entry in the symbols table, so these will be turned
+ // into appropriate outputs.
+ return makeSpan(
+ ["mord", "mspace"],
+ [buildCommon.mathsym(group.value, group.mode)]
+ );
+ } else {
+ // Other kinds of spaces are of arbitrary width. We use CSS to
+ // generate these.
+ return makeSpan(
+ ["mord", "mspace",
+ buildCommon.spacingFunctions[group.value].className]);
+ }
+};
+
+groupTypes.llap = function(group, options, prev) {
+ var inner = makeSpan(
+ ["inner"], [buildGroup(group.value.body, options.reset())]);
+ var fix = makeSpan(["fix"], []);
+ return makeSpan(
+ ["llap", options.style.cls()], [inner, fix]);
+};
+
+groupTypes.rlap = function(group, options, prev) {
+ var inner = makeSpan(
+ ["inner"], [buildGroup(group.value.body, options.reset())]);
+ var fix = makeSpan(["fix"], []);
+ return makeSpan(
+ ["rlap", options.style.cls()], [inner, fix]);
+};
+
+groupTypes.op = function(group, options, prev) {
+ // Operators are handled in the TeXbook pg. 443-444, rule 13(a).
+ var supGroup;
+ var subGroup;
+ var hasLimits = false;
+ if (group.type === "supsub" ) {
+ // If we have limits, supsub will pass us its group to handle. Pull
+ // out the superscript and subscript and set the group to the op in
+ // its base.
+ supGroup = group.value.sup;
+ subGroup = group.value.sub;
+ group = group.value.base;
+ hasLimits = true;
+ }
+
+ // Most operators have a large successor symbol, but these don't.
+ var noSuccessor = [
+ "\\smallint",
+ ];
+
+ var large = false;
+ if (options.style.size === Style.DISPLAY.size &&
+ group.value.symbol &&
+ !utils.contains(noSuccessor, group.value.body)) {
+
+ // Most symbol operators get larger in displaystyle (rule 13)
+ large = true;
+ }
+
+ var base;
+ var baseShift = 0;
+ var slant = 0;
+ if (group.value.symbol) {
+ // If this is a symbol, create the symbol.
+ var style = large ? "Size2-Regular" : "Size1-Regular";
+ base = buildCommon.makeSymbol(
+ group.value.body, style, "math", options.getColor(),
+ ["op-symbol", large ? "large-op" : "small-op", "mop"]);
+
+ // Shift the symbol so its center lies on the axis (rule 13). It
+ // appears that our fonts have the centers of the symbols already
+ // almost on the axis, so these numbers are very small. Note we
+ // don't actually apply this here, but instead it is used either in
+ // the vlist creation or separately when there are no limits.
+ baseShift = (base.height - base.depth) / 2 -
+ fontMetrics.metrics.axisHeight *
+ options.style.sizeMultiplier;
+
+ // The slant of the symbol is just its italic correction.
+ slant = base.italic;
+ } else {
+ // Otherwise, this is a text operator. Build the text from the
+ // operator's name.
+ // TODO(emily): Add a space in the middle of some of these
+ // operators, like \limsup
+ var output = [];
+ for (var i = 1; i < group.value.body.length; i++) {
+ output.push(buildCommon.mathsym(group.value.body[i], group.mode));
+ }
+ base = makeSpan(["mop"], output, options.getColor());
+ }
+
+ if (hasLimits) {
+ // IE 8 clips \int if it is in a display: inline-block. We wrap it
+ // in a new span so it is an inline, and works.
+ base = makeSpan([], [base]);
+
+ var supmid;
+ var supKern;
+ var submid;
+ var subKern;
+ // We manually have to handle the superscripts and subscripts. This,
+ // aside from the kern calculations, is copied from supsub.
+ if (supGroup) {
+ var sup = buildGroup(
+ supGroup, options.withStyle(options.style.sup()));
+ supmid = makeSpan(
+ [options.style.reset(), options.style.sup().cls()], [sup]);
+
+ supKern = Math.max(
+ fontMetrics.metrics.bigOpSpacing1,
+ fontMetrics.metrics.bigOpSpacing3 - sup.depth);
+ }
+
+ if (subGroup) {
+ var sub = buildGroup(
+ subGroup, options.withStyle(options.style.sub()));
+ submid = makeSpan(
+ [options.style.reset(), options.style.sub().cls()],
+ [sub]);
+
+ subKern = Math.max(
+ fontMetrics.metrics.bigOpSpacing2,
+ fontMetrics.metrics.bigOpSpacing4 - sub.height);
+ }
+
+ // Build the final group as a vlist of the possible subscript, base,
+ // and possible superscript.
+ var finalGroup;
+ var top;
+ var bottom;
+ if (!supGroup) {
+ top = base.height - baseShift;
+
+ finalGroup = buildCommon.makeVList([
+ {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
+ {type: "elem", elem: submid},
+ {type: "kern", size: subKern},
+ {type: "elem", elem: base},
+ ], "top", top, options);
+
+ // Here, we shift the limits by the slant of the symbol. Note
+ // that we are supposed to shift the limits by 1/2 of the slant,
+ // but since we are centering the limits adding a full slant of
+ // margin will shift by 1/2 that.
+ finalGroup.children[0].style.marginLeft = -slant + "em";
+ } else if (!subGroup) {
+ bottom = base.depth + baseShift;
+
+ finalGroup = buildCommon.makeVList([
+ {type: "elem", elem: base},
+ {type: "kern", size: supKern},
+ {type: "elem", elem: supmid},
+ {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
+ ], "bottom", bottom, options);
+
+ // See comment above about slants
+ finalGroup.children[1].style.marginLeft = slant + "em";
+ } else if (!supGroup && !subGroup) {
+ // This case probably shouldn't occur (this would mean the
+ // supsub was sending us a group with no superscript or
+ // subscript) but be safe.
+ return base;
+ } else {
+ bottom = fontMetrics.metrics.bigOpSpacing5 +
+ submid.height + submid.depth +
+ subKern +
+ base.depth + baseShift;
+
+ finalGroup = buildCommon.makeVList([
+ {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
+ {type: "elem", elem: submid},
+ {type: "kern", size: subKern},
+ {type: "elem", elem: base},
+ {type: "kern", size: supKern},
+ {type: "elem", elem: supmid},
+ {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
+ ], "bottom", bottom, options);
+
+ // See comment above about slants
+ finalGroup.children[0].style.marginLeft = -slant + "em";
+ finalGroup.children[2].style.marginLeft = slant + "em";
+ }
+
+ return makeSpan(["mop", "op-limits"], [finalGroup]);
+ } else {
+ if (group.value.symbol) {
+ base.style.top = baseShift + "em";
+ }
+
+ return base;
+ }
+};
+
+groupTypes.katex = function(group, options, prev) {
+ // The KaTeX logo. The offsets for the K and a were chosen to look
+ // good, but the offsets for the T, E, and X were taken from the
+ // definition of \TeX in TeX (see TeXbook pg. 356)
+ var k = makeSpan(
+ ["k"], [buildCommon.mathsym("K", group.mode)]);
+ var a = makeSpan(
+ ["a"], [buildCommon.mathsym("A", group.mode)]);
+
+ a.height = (a.height + 0.2) * 0.75;
+ a.depth = (a.height - 0.2) * 0.75;
+
+ var t = makeSpan(
+ ["t"], [buildCommon.mathsym("T", group.mode)]);
+ var e = makeSpan(
+ ["e"], [buildCommon.mathsym("E", group.mode)]);
+
+ e.height = (e.height - 0.2155);
+ e.depth = (e.depth + 0.2155);
+
+ var x = makeSpan(
+ ["x"], [buildCommon.mathsym("X", group.mode)]);
+
+ return makeSpan(
+ ["katex-logo", "mord"], [k, a, t, e, x], options.getColor());
+};
+
+groupTypes.overline = function(group, options, prev) {
+ // Overlines are handled in the TeXbook pg 443, Rule 9.
+
+ // Build the inner group in the cramped style.
+ var innerGroup = buildGroup(group.value.body,
+ options.withStyle(options.style.cramp()));
+
+ var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
+ options.style.sizeMultiplier;
+
+ // Create the line above the body
+ var line = makeSpan(
+ [options.style.reset(), Style.TEXT.cls(), "overline-line"]);
+ line.height = ruleWidth;
+ line.maxFontSize = 1.0;
+
+ // Generate the vlist, with the appropriate kerns
+ var vlist = buildCommon.makeVList([
+ {type: "elem", elem: innerGroup},
+ {type: "kern", size: 3 * ruleWidth},
+ {type: "elem", elem: line},
+ {type: "kern", size: ruleWidth},
+ ], "firstBaseline", null, options);
+
+ return makeSpan(["overline", "mord"], [vlist], options.getColor());
+};
+
+groupTypes.underline = function(group, options, prev) {
+ // Underlines are handled in the TeXbook pg 443, Rule 10.
+
+ // Build the inner group.
+ var innerGroup = buildGroup(group.value.body, options);
+
+ var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
+ options.style.sizeMultiplier;
+
+ // Create the line above the body
+ var line = makeSpan(
+ [options.style.reset(), Style.TEXT.cls(), "underline-line"]);
+ line.height = ruleWidth;
+ line.maxFontSize = 1.0;
+
+ // Generate the vlist, with the appropriate kerns
+ var vlist = buildCommon.makeVList([
+ {type: "kern", size: ruleWidth},
+ {type: "elem", elem: line},
+ {type: "kern", size: 3 * ruleWidth},
+ {type: "elem", elem: innerGroup},
+ ], "top", innerGroup.height, options);
+
+ return makeSpan(["underline", "mord"], [vlist], options.getColor());
+};
+
+groupTypes.sqrt = function(group, options, prev) {
+ // Square roots are handled in the TeXbook pg. 443, Rule 11.
+
+ // First, we do the same steps as in overline to build the inner group
+ // and line
+ var inner = buildGroup(group.value.body,
+ options.withStyle(options.style.cramp()));
+
+ var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
+ options.style.sizeMultiplier;
+
+ var line = makeSpan(
+ [options.style.reset(), Style.TEXT.cls(), "sqrt-line"], [],
+ options.getColor());
+ line.height = ruleWidth;
+ line.maxFontSize = 1.0;
+
+ var phi = ruleWidth;
+ if (options.style.id < Style.TEXT.id) {
+ phi = fontMetrics.metrics.xHeight;
+ }
+
+ // Calculate the clearance between the body and line
+ var lineClearance = ruleWidth + phi / 4;
+
+ var innerHeight =
+ (inner.height + inner.depth) * options.style.sizeMultiplier;
+ var minDelimiterHeight = innerHeight + lineClearance + ruleWidth;
+
+ // Create a \surd delimiter of the required minimum size
+ var delim = makeSpan(["sqrt-sign"], [
+ delimiter.customSizedDelim("\\surd", minDelimiterHeight,
+ false, options, group.mode)],
+ options.getColor());
+
+ var delimDepth = (delim.height + delim.depth) - ruleWidth;
+
+ // Adjust the clearance based on the delimiter size
+ if (delimDepth > inner.height + inner.depth + lineClearance) {
+ lineClearance =
+ (lineClearance + delimDepth - inner.height - inner.depth) / 2;
+ }
+
+ // Shift the delimiter so that its top lines up with the top of the line
+ var delimShift = -(inner.height + lineClearance + ruleWidth) + delim.height;
+ delim.style.top = delimShift + "em";
+ delim.height -= delimShift;
+ delim.depth += delimShift;
+
+ // We add a special case here, because even when `inner` is empty, we
+ // still get a line. So, we use a simple heuristic to decide if we
+ // should omit the body entirely. (note this doesn't work for something
+ // like `\sqrt{\rlap{x}}`, but if someone is doing that they deserve for
+ // it not to work.
+ var body;
+ if (inner.height === 0 && inner.depth === 0) {
+ body = makeSpan();
+ } else {
+ body = buildCommon.makeVList([
+ {type: "elem", elem: inner},
+ {type: "kern", size: lineClearance},
+ {type: "elem", elem: line},
+ {type: "kern", size: ruleWidth},
+ ], "firstBaseline", null, options);
+ }
+
+ if (!group.value.index) {
+ return makeSpan(["sqrt", "mord"], [delim, body]);
+ } else {
+ // Handle the optional root index
+
+ // The index is always in scriptscript style
+ var root = buildGroup(
+ group.value.index,
+ options.withStyle(Style.SCRIPTSCRIPT));
+ var rootWrap = makeSpan(
+ [options.style.reset(), Style.SCRIPTSCRIPT.cls()],
+ [root]);
+
+ // Figure out the height and depth of the inner part
+ var innerRootHeight = Math.max(delim.height, body.height);
+ var innerRootDepth = Math.max(delim.depth, body.depth);
+
+ // The amount the index is shifted by. This is taken from the TeX
+ // source, in the definition of `\r@@t`.
+ var toShift = 0.6 * (innerRootHeight - innerRootDepth);
+
+ // Build a VList with the superscript shifted up correctly
+ var rootVList = buildCommon.makeVList(
+ [{type: "elem", elem: rootWrap}],
+ "shift", -toShift, options);
+ // Add a class surrounding it so we can add on the appropriate
+ // kerning
+ var rootVListWrap = makeSpan(["root"], [rootVList]);
+
+ return makeSpan(["sqrt", "mord"], [rootVListWrap, delim, body]);
+ }
+};
+
+groupTypes.sizing = function(group, options, prev) {
+ // Handle sizing operators like \Huge. Real TeX doesn't actually allow
+ // these functions inside of math expressions, so we do some special
+ // handling.
+ var inner = buildExpression(group.value.value,
+ options.withSize(group.value.size), prev);
+
+ var span = makeSpan(["mord"],
+ [makeSpan(["sizing", "reset-" + options.size, group.value.size,
+ options.style.cls()],
+ inner)]);
+
+ // Calculate the correct maxFontSize manually
+ var fontSize = buildCommon.sizingMultiplier[group.value.size];
+ span.maxFontSize = fontSize * options.style.sizeMultiplier;
+
+ return span;
+};
+
+groupTypes.styling = function(group, options, prev) {
+ // Style changes are handled in the TeXbook on pg. 442, Rule 3.
+
+ // Figure out what style we're changing to.
+ var style = {
+ "display": Style.DISPLAY,
+ "text": Style.TEXT,
+ "script": Style.SCRIPT,
+ "scriptscript": Style.SCRIPTSCRIPT,
+ };
+
+ var newStyle = style[group.value.style];
+
+ // Build the inner expression in the new style.
+ var inner = buildExpression(
+ group.value.value, options.withStyle(newStyle), prev);
+
+ return makeSpan([options.style.reset(), newStyle.cls()], inner);
+};
+
+groupTypes.font = function(group, options, prev) {
+ var font = group.value.font;
+ return buildGroup(group.value.body, options.withFont(font), prev);
+};
+
+groupTypes.delimsizing = function(group, options, prev) {
+ var delim = group.value.value;
+
+ if (delim === ".") {
+ // Empty delimiters still count as elements, even though they don't
+ // show anything.
+ return makeSpan([groupToType[group.value.delimType]]);
+ }
+
+ // Use delimiter.sizedDelim to generate the delimiter.
+ return makeSpan(
+ [groupToType[group.value.delimType]],
+ [delimiter.sizedDelim(
+ delim, group.value.size, options, group.mode)]);
+};
+
+groupTypes.leftright = function(group, options, prev) {
+ // Build the inner expression
+ var inner = buildExpression(group.value.body, options.reset());
+
+ var innerHeight = 0;
+ var innerDepth = 0;
+
+ // Calculate its height and depth
+ for (var i = 0; i < inner.length; i++) {
+ innerHeight = Math.max(inner[i].height, innerHeight);
+ innerDepth = Math.max(inner[i].depth, innerDepth);
+ }
+
+ // The size of delimiters is the same, regardless of what style we are
+ // in. Thus, to correctly calculate the size of delimiter we need around
+ // a group, we scale down the inner size based on the size.
+ innerHeight *= options.style.sizeMultiplier;
+ innerDepth *= options.style.sizeMultiplier;
+
+ var leftDelim;
+ if (group.value.left === ".") {
+ // Empty delimiters in \left and \right make null delimiter spaces.
+ leftDelim = makeNullDelimiter(options);
+ } else {
+ // Otherwise, use leftRightDelim to generate the correct sized
+ // delimiter.
+ leftDelim = delimiter.leftRightDelim(
+ group.value.left, innerHeight, innerDepth, options,
+ group.mode);
+ }
+ // Add it to the beginning of the expression
+ inner.unshift(leftDelim);
+
+ var rightDelim;
+ // Same for the right delimiter
+ if (group.value.right === ".") {
+ rightDelim = makeNullDelimiter(options);
+ } else {
+ rightDelim = delimiter.leftRightDelim(
+ group.value.right, innerHeight, innerDepth, options,
+ group.mode);
+ }
+ // Add it to the end of the expression.
+ inner.push(rightDelim);
+
+ return makeSpan(
+ ["minner", options.style.cls()], inner, options.getColor());
+};
+
+groupTypes.rule = function(group, options, prev) {
+ // Make an empty span for the rule
+ var rule = makeSpan(["mord", "rule"], [], options.getColor());
+
+ // Calculate the shift, width, and height of the rule, and account for units
+ var shift = 0;
+ if (group.value.shift) {
+ shift = group.value.shift.number;
+ if (group.value.shift.unit === "ex") {
+ shift *= fontMetrics.metrics.xHeight;
+ }
+ }
+
+ var width = group.value.width.number;
+ if (group.value.width.unit === "ex") {
+ width *= fontMetrics.metrics.xHeight;
+ }
+
+ var height = group.value.height.number;
+ if (group.value.height.unit === "ex") {
+ height *= fontMetrics.metrics.xHeight;
+ }
+
+ // The sizes of rules are absolute, so make it larger if we are in a
+ // smaller style.
+ shift /= options.style.sizeMultiplier;
+ width /= options.style.sizeMultiplier;
+ height /= options.style.sizeMultiplier;
+
+ // Style the rule to the right size
+ rule.style.borderRightWidth = width + "em";
+ rule.style.borderTopWidth = height + "em";
+ rule.style.bottom = shift + "em";
+
+ // Record the height and width
+ rule.width = width;
+ rule.height = height + shift;
+ rule.depth = -shift;
+
+ return rule;
+};
+
+groupTypes.kern = function(group, options, prev) {
+ // Make an empty span for the rule
+ var rule = makeSpan(["mord", "rule"], [], options.getColor());
+
+ var dimension = 0;
+ if (group.value.dimension) {
+ dimension = group.value.dimension.number;
+ if (group.value.dimension.unit === "ex") {
+ dimension *= fontMetrics.metrics.xHeight;
+ }
+ }
+
+ dimension /= options.style.sizeMultiplier;
+
+ rule.style.marginLeft = dimension + "em";
+
+ return rule;
+};
+
+groupTypes.accent = function(group, options, prev) {
+ // Accents are handled in the TeXbook pg. 443, rule 12.
+ var base = group.value.base;
+
+ var supsubGroup;
+ if (group.type === "supsub") {
+ // If our base is a character box, and we have superscripts and
+ // subscripts, the supsub will defer to us. In particular, we want
+ // to attach the superscripts and subscripts to the inner body (so
+ // that the position of the superscripts and subscripts won't be
+ // affected by the height of the accent). We accomplish this by
+ // sticking the base of the accent into the base of the supsub, and
+ // rendering that, while keeping track of where the accent is.
+
+ // The supsub group is the group that was passed in
+ var supsub = group;
+ // The real accent group is the base of the supsub group
+ group = supsub.value.base;
+ // The character box is the base of the accent group
+ base = group.value.base;
+ // Stick the character box into the base of the supsub group
+ supsub.value.base = base;
+
+ // Rerender the supsub group with its new base, and store that
+ // result.
+ supsubGroup = buildGroup(
+ supsub, options.reset(), prev);
+ }
+
+ // Build the base group
+ var body = buildGroup(
+ base, options.withStyle(options.style.cramp()));
+
+ // Calculate the skew of the accent. This is based on the line "If the
+ // nucleus is not a single character, let s = 0; otherwise set s to the
+ // kern amount for the nucleus followed by the \skewchar of its font."
+ // Note that our skew metrics are just the kern between each character
+ // and the skewchar.
+ var skew;
+ if (isCharacterBox(base)) {
+ // If the base is a character box, then we want the skew of the
+ // innermost character. To do that, we find the innermost character:
+ var baseChar = getBaseElem(base);
+ // Then, we render its group to get the symbol inside it
+ var baseGroup = buildGroup(
+ baseChar, options.withStyle(options.style.cramp()));
+ // Finally, we pull the skew off of the symbol.
+ skew = baseGroup.skew;
+ // Note that we now throw away baseGroup, because the layers we
+ // removed with getBaseElem might contain things like \color which
+ // we can't get rid of.
+ // TODO(emily): Find a better way to get the skew
+ } else {
+ skew = 0;
+ }
+
+ // calculate the amount of space between the body and the accent
+ var clearance = Math.min(body.height, fontMetrics.metrics.xHeight);
+
+ // Build the accent
+ var accent = buildCommon.makeSymbol(
+ group.value.accent, "Main-Regular", "math", options.getColor());
+ // Remove the italic correction of the accent, because it only serves to
+ // shift the accent over to a place we don't want.
+ accent.italic = 0;
+
+ // The \vec character that the fonts use is a combining character, and
+ // thus shows up much too far to the left. To account for this, we add a
+ // specific class which shifts the accent over to where we want it.
+ // TODO(emily): Fix this in a better way, like by changing the font
+ var vecClass = group.value.accent === "\\vec" ? "accent-vec" : null;
+
+ var accentBody = makeSpan(["accent-body", vecClass], [
+ makeSpan([], [accent])]);
+
+ accentBody = buildCommon.makeVList([
+ {type: "elem", elem: body},
+ {type: "kern", size: -clearance},
+ {type: "elem", elem: accentBody},
+ ], "firstBaseline", null, options);
+
+ // Shift the accent over by the skew. Note we shift by twice the skew
+ // because we are centering the accent, so by adding 2*skew to the left,
+ // we shift it to the right by 1*skew.
+ accentBody.children[1].style.marginLeft = 2 * skew + "em";
+
+ var accentWrap = makeSpan(["mord", "accent"], [accentBody]);
+
+ if (supsubGroup) {
+ // Here, we replace the "base" child of the supsub with our newly
+ // generated accent.
+ supsubGroup.children[0] = accentWrap;
+
+ // Since we don't rerun the height calculation after replacing the
+ // accent, we manually recalculate height.
+ supsubGroup.height = Math.max(accentWrap.height, supsubGroup.height);
+
+ // Accents should always be ords, even when their innards are not.
+ supsubGroup.classes[0] = "mord";
+
+ return supsubGroup;
+ } else {
+ return accentWrap;
+ }
+};
+
+groupTypes.phantom = function(group, options, prev) {
+ var elements = buildExpression(
+ group.value.value,
+ options.withPhantom(),
+ prev
+ );
+
+ // \phantom isn't supposed to affect the elements it contains.
+ // See "color" for more details.
+ return new buildCommon.makeFragment(elements);
+};
+
+/**
+ * buildGroup is the function that takes a group and calls the correct groupType
+ * function for it. It also handles the interaction of size and style changes
+ * between parents and children.
+ */
+var buildGroup = function(group, options, prev) {
+ if (!group) {
+ return makeSpan();
+ }
+
+ if (groupTypes[group.type]) {
+ // Call the groupTypes function
+ var groupNode = groupTypes[group.type](group, options, prev);
+ var multiplier;
+
+ // If the style changed between the parent and the current group,
+ // account for the size difference
+ if (options.style !== options.parentStyle) {
+ multiplier = options.style.sizeMultiplier /
+ options.parentStyle.sizeMultiplier;
+
+ groupNode.height *= multiplier;
+ groupNode.depth *= multiplier;
+ }
+
+ // If the size changed between the parent and the current group, account
+ // for that size difference.
+ if (options.size !== options.parentSize) {
+ multiplier = buildCommon.sizingMultiplier[options.size] /
+ buildCommon.sizingMultiplier[options.parentSize];
+
+ groupNode.height *= multiplier;
+ groupNode.depth *= multiplier;
+ }
+
+ return groupNode;
+ } else {
+ throw new ParseError(
+ "Got group of unknown type: '" + group.type + "'");
+ }
+};
+
+/**
+ * Take an entire parse tree, and build it into an appropriate set of HTML
+ * nodes.
+ */
+var buildHTML = function(tree, options) {
+ // buildExpression is destructive, so we need to make a clone
+ // of the incoming tree so that it isn't accidentally changed
+ tree = JSON.parse(JSON.stringify(tree));
+
+ // Build the expression contained in the tree
+ var expression = buildExpression(tree, options);
+ var body = makeSpan(["base", options.style.cls()], expression);
+
+ // Add struts, which ensure that the top of the HTML element falls at the
+ // height of the expression, and the bottom of the HTML element falls at the
+ // depth of the expression.
+ var topStrut = makeSpan(["strut"]);
+ var bottomStrut = makeSpan(["strut", "bottom"]);
+
+ topStrut.style.height = body.height + "em";
+ bottomStrut.style.height = (body.height + body.depth) + "em";
+ // We'd like to use `vertical-align: top` but in IE 9 this lowers the
+ // baseline of the box to the bottom of this strut (instead staying in the
+ // normal place) so we use an absolute value for vertical-align instead
+ bottomStrut.style.verticalAlign = -body.depth + "em";
+
+ // Wrap the struts and body together
+ var htmlNode = makeSpan(["katex-html"], [topStrut, bottomStrut, body]);
+
+ htmlNode.setAttribute("aria-hidden", "true");
+
+ return htmlNode;
+};
+
+module.exports = buildHTML;
+
+},{"./ParseError":6,"./Style":9,"./buildCommon":10,"./delimiter":14,"./domTree":15,"./fontMetrics":17,"./utils":25}],12:[function(require,module,exports){
+/**
+ * This file converts a parse tree into a cooresponding MathML tree. The main
+ * entry point is the `buildMathML` function, which takes a parse tree from the
+ * parser.
+ */
+
+var buildCommon = require("./buildCommon");
+var fontMetrics = require("./fontMetrics");
+var mathMLTree = require("./mathMLTree");
+var ParseError = require("./ParseError");
+var symbols = require("./symbols");
+var utils = require("./utils");
+
+var makeSpan = buildCommon.makeSpan;
+var fontMap = buildCommon.fontMap;
+
+/**
+ * Takes a symbol and converts it into a MathML text node after performing
+ * optional replacement from symbols.js.
+ */
+var makeText = function(text, mode) {
+ if (symbols[mode][text] && symbols[mode][text].replace) {
+ text = symbols[mode][text].replace;
+ }
+
+ return new mathMLTree.TextNode(text);
+};
+
+/**
+ * Returns the math variant as a string or null if none is required.
+ */
+var getVariant = function(group, options) {
+ var font = options.font;
+ if (!font) {
+ return null;
+ }
+
+ var mode = group.mode;
+ if (font === "mathit") {
+ return "italic";
+ }
+
+ var value = group.value;
+ if (utils.contains(["\\imath", "\\jmath"], value)) {
+ return null;
+ }
+
+ if (symbols[mode][value] && symbols[mode][value].replace) {
+ value = symbols[mode][value].replace;
+ }
+
+ var fontName = fontMap[font].fontName;
+ if (fontMetrics.getCharacterMetrics(value, fontName)) {
+ return fontMap[options.font].variant;
+ }
+
+ return null;
+};
+
+/**
+ * Functions for handling the different types of groups found in the parse
+ * tree. Each function should take a parse group and return a MathML node.
+ */
+var groupTypes = {};
+
+groupTypes.mathord = function(group, options) {
+ var node = new mathMLTree.MathNode(
+ "mi",
+ [makeText(group.value, group.mode)]);
+
+ var variant = getVariant(group, options);
+ if (variant) {
+ node.setAttribute("mathvariant", variant);
+ }
+ return node;
+};
+
+groupTypes.textord = function(group, options) {
+ var text = makeText(group.value, group.mode);
+
+ var variant = getVariant(group, options) || "normal";
+
+ var node;
+ if (/[0-9]/.test(group.value)) {
+ // TODO(kevinb) merge adjacent <mn> nodes
+ // do it as a post processing step
+ node = new mathMLTree.MathNode("mn", [text]);
+ if (options.font) {
+ node.setAttribute("mathvariant", variant);
+ }
+ } else {
+ node = new mathMLTree.MathNode("mi", [text]);
+ node.setAttribute("mathvariant", variant);
+ }
+
+ return node;
+};
+
+groupTypes.bin = function(group) {
+ var node = new mathMLTree.MathNode(
+ "mo", [makeText(group.value, group.mode)]);
+
+ return node;
+};
+
+groupTypes.rel = function(group) {
+ var node = new mathMLTree.MathNode(
+ "mo", [makeText(group.value, group.mode)]);
+
+ return node;
+};
+
+groupTypes.open = function(group) {
+ var node = new mathMLTree.MathNode(
+ "mo", [makeText(group.value, group.mode)]);
+
+ return node;
+};
+
+groupTypes.close = function(group) {
+ var node = new mathMLTree.MathNode(
+ "mo", [makeText(group.value, group.mode)]);
+
+ return node;
+};
+
+groupTypes.inner = function(group) {
+ var node = new mathMLTree.MathNode(
+ "mo", [makeText(group.value, group.mode)]);
+
+ return node;
+};
+
+groupTypes.punct = function(group) {
+ var node = new mathMLTree.MathNode(
+ "mo", [makeText(group.value, group.mode)]);
+
+ node.setAttribute("separator", "true");
+
+ return node;
+};
+
+groupTypes.ordgroup = function(group, options) {
+ var inner = buildExpression(group.value, options);
+
+ var node = new mathMLTree.MathNode("mrow", inner);
+
+ return node;
+};
+
+groupTypes.text = function(group, options) {
+ var inner = buildExpression(group.value.body, options);
+
+ var node = new mathMLTree.MathNode("mtext", inner);
+
+ return node;
+};
+
+groupTypes.color = function(group, options) {
+ var inner = buildExpression(group.value.value, options);
+
+ var node = new mathMLTree.MathNode("mstyle", inner);
+
+ node.setAttribute("mathcolor", group.value.color);
+
+ return node;
+};
+
+groupTypes.supsub = function(group, options) {
+ var children = [buildGroup(group.value.base, options)];
+
+ if (group.value.sub) {
+ children.push(buildGroup(group.value.sub, options));
+ }
+
+ if (group.value.sup) {
+ children.push(buildGroup(group.value.sup, options));
+ }
+
+ var nodeType;
+ if (!group.value.sub) {
+ nodeType = "msup";
+ } else if (!group.value.sup) {
+ nodeType = "msub";
+ } else {
+ nodeType = "msubsup";
+ }
+
+ var node = new mathMLTree.MathNode(nodeType, children);
+
+ return node;
+};
+
+groupTypes.genfrac = function(group, options) {
+ var node = new mathMLTree.MathNode(
+ "mfrac",
+ [buildGroup(group.value.numer, options),
+ buildGroup(group.value.denom, options)]);
+
+ if (!group.value.hasBarLine) {
+ node.setAttribute("linethickness", "0px");
+ }
+
+ if (group.value.leftDelim != null || group.value.rightDelim != null) {
+ var withDelims = [];
+
+ if (group.value.leftDelim != null) {
+ var leftOp = new mathMLTree.MathNode(
+ "mo", [new mathMLTree.TextNode(group.value.leftDelim)]);
+
+ leftOp.setAttribute("fence", "true");
+
+ withDelims.push(leftOp);
+ }
+
+ withDelims.push(node);
+
+ if (group.value.rightDelim != null) {
+ var rightOp = new mathMLTree.MathNode(
+ "mo", [new mathMLTree.TextNode(group.value.rightDelim)]);
+
+ rightOp.setAttribute("fence", "true");
+
+ withDelims.push(rightOp);
+ }
+
+ var outerNode = new mathMLTree.MathNode("mrow", withDelims);
+
+ return outerNode;
+ }
+
+ return node;
+};
+
+groupTypes.array = function(group, options) {
+ return new mathMLTree.MathNode(
+ "mtable", group.value.body.map(function(row) {
+ return new mathMLTree.MathNode(
+ "mtr", row.map(function(cell) {
+ return new mathMLTree.MathNode(
+ "mtd", [buildGroup(cell, options)]);
+ }));
+ }));
+};
+
+groupTypes.sqrt = function(group, options) {
+ var node;
+ if (group.value.index) {
+ node = new mathMLTree.MathNode(
+ "mroot", [
+ buildGroup(group.value.body, options),
+ buildGroup(group.value.index, options),
+ ]);
+ } else {
+ node = new mathMLTree.MathNode(
+ "msqrt", [buildGroup(group.value.body, options)]);
+ }
+
+ return node;
+};
+
+groupTypes.leftright = function(group, options) {
+ var inner = buildExpression(group.value.body, options);
+
+ if (group.value.left !== ".") {
+ var leftNode = new mathMLTree.MathNode(
+ "mo", [makeText(group.value.left, group.mode)]);
+
+ leftNode.setAttribute("fence", "true");
+
+ inner.unshift(leftNode);
+ }
+
+ if (group.value.right !== ".") {
+ var rightNode = new mathMLTree.MathNode(
+ "mo", [makeText(group.value.right, group.mode)]);
+
+ rightNode.setAttribute("fence", "true");
+
+ inner.push(rightNode);
+ }
+
+ var outerNode = new mathMLTree.MathNode("mrow", inner);
+
+ return outerNode;
+};
+
+groupTypes.accent = function(group, options) {
+ var accentNode = new mathMLTree.MathNode(
+ "mo", [makeText(group.value.accent, group.mode)]);
+
+ var node = new mathMLTree.MathNode(
+ "mover",
+ [buildGroup(group.value.base, options),
+ accentNode]);
+
+ node.setAttribute("accent", "true");
+
+ return node;
+};
+
+groupTypes.spacing = function(group) {
+ var node;
+
+ if (group.value === "\\ " || group.value === "\\space" ||
+ group.value === " " || group.value === "~") {
+ node = new mathMLTree.MathNode(
+ "mtext", [new mathMLTree.TextNode("\u00a0")]);
+ } else {
+ node = new mathMLTree.MathNode("mspace");
+
+ node.setAttribute(
+ "width", buildCommon.spacingFunctions[group.value].size);
+ }
+
+ return node;
+};
+
+groupTypes.op = function(group) {
+ var node;
+
+ // TODO(emily): handle big operators using the `largeop` attribute
+
+ if (group.value.symbol) {
+ // This is a symbol. Just add the symbol.
+ node = new mathMLTree.MathNode(
+ "mo", [makeText(group.value.body, group.mode)]);
+ } else {
+ // This is a text operator. Add all of the characters from the
+ // operator's name.
+ // TODO(emily): Add a space in the middle of some of these
+ // operators, like \limsup.
+ node = new mathMLTree.MathNode(
+ "mi", [new mathMLTree.TextNode(group.value.body.slice(1))]);
+ }
+
+ return node;
+};
+
+groupTypes.katex = function(group) {
+ var node = new mathMLTree.MathNode(
+ "mtext", [new mathMLTree.TextNode("KaTeX")]);
+
+ return node;
+};
+
+groupTypes.font = function(group, options) {
+ var font = group.value.font;
+ return buildGroup(group.value.body, options.withFont(font));
+};
+
+groupTypes.delimsizing = function(group) {
+ var children = [];
+
+ if (group.value.value !== ".") {
+ children.push(makeText(group.value.value, group.mode));
+ }
+
+ var node = new mathMLTree.MathNode("mo", children);
+
+ if (group.value.delimType === "open" ||
+ group.value.delimType === "close") {
+ // Only some of the delimsizing functions act as fences, and they
+ // return "open" or "close" delimTypes.
+ node.setAttribute("fence", "true");
+ } else {
+ // Explicitly disable fencing if it's not a fence, to override the
+ // defaults.
+ node.setAttribute("fence", "false");
+ }
+
+ return node;
+};
+
+groupTypes.styling = function(group, options) {
+ var inner = buildExpression(group.value.value, options);
+
+ var node = new mathMLTree.MathNode("mstyle", inner);
+
+ var styleAttributes = {
+ "display": ["0", "true"],
+ "text": ["0", "false"],
+ "script": ["1", "false"],
+ "scriptscript": ["2", "false"],
+ };
+
+ var attr = styleAttributes[group.value.style];
+
+ node.setAttribute("scriptlevel", attr[0]);
+ node.setAttribute("displaystyle", attr[1]);
+
+ return node;
+};
+
+groupTypes.sizing = function(group, options) {
+ var inner = buildExpression(group.value.value, options);
+
+ var node = new mathMLTree.MathNode("mstyle", inner);
+
+ // TODO(emily): This doesn't produce the correct size for nested size
+ // changes, because we don't keep state of what style we're currently
+ // in, so we can't reset the size to normal before changing it. Now
+ // that we're passing an options parameter we should be able to fix
+ // this.
+ node.setAttribute(
+ "mathsize", buildCommon.sizingMultiplier[group.value.size] + "em");
+
+ return node;
+};
+
+groupTypes.overline = function(group, options) {
+ var operator = new mathMLTree.MathNode(
+ "mo", [new mathMLTree.TextNode("\u203e")]);
+ operator.setAttribute("stretchy", "true");
+
+ var node = new mathMLTree.MathNode(
+ "mover",
+ [buildGroup(group.value.body, options),
+ operator]);
+ node.setAttribute("accent", "true");
+
+ return node;
+};
+
+groupTypes.underline = function(group, options) {
+ var operator = new mathMLTree.MathNode(
+ "mo", [new mathMLTree.TextNode("\u203e")]);
+ operator.setAttribute("stretchy", "true");
+
+ var node = new mathMLTree.MathNode(
+ "munder",
+ [buildGroup(group.value.body, options),
+ operator]);
+ node.setAttribute("accentunder", "true");
+
+ return node;
+};
+
+groupTypes.rule = function(group) {
+ // TODO(emily): Figure out if there's an actual way to draw black boxes
+ // in MathML.
+ var node = new mathMLTree.MathNode("mrow");
+
+ return node;
+};
+
+groupTypes.kern = function(group) {
+ // TODO(kevin): Figure out if there's a way to add space in MathML
+ var node = new mathMLTree.MathNode("mrow");
+
+ return node;
+};
+
+groupTypes.llap = function(group, options) {
+ var node = new mathMLTree.MathNode(
+ "mpadded", [buildGroup(group.value.body, options)]);
+
+ node.setAttribute("lspace", "-1width");
+ node.setAttribute("width", "0px");
+
+ return node;
+};
+
+groupTypes.rlap = function(group, options) {
+ var node = new mathMLTree.MathNode(
+ "mpadded", [buildGroup(group.value.body, options)]);
+
+ node.setAttribute("width", "0px");
+
+ return node;
+};
+
+groupTypes.phantom = function(group, options, prev) {
+ var inner = buildExpression(group.value.value, options);
+ return new mathMLTree.MathNode("mphantom", inner);
+};
+
+/**
+ * Takes a list of nodes, builds them, and returns a list of the generated
+ * MathML nodes. A little simpler than the HTML version because we don't do any
+ * previous-node handling.
+ */
+var buildExpression = function(expression, options) {
+ var groups = [];
+ for (var i = 0; i < expression.length; i++) {
+ var group = expression[i];
+ groups.push(buildGroup(group, options));
+ }
+ return groups;
+};
+
+/**
+ * Takes a group from the parser and calls the appropriate groupTypes function
+ * on it to produce a MathML node.
+ */
+var buildGroup = function(group, options) {
+ if (!group) {
+ return new mathMLTree.MathNode("mrow");
+ }
+
+ if (groupTypes[group.type]) {
+ // Call the groupTypes function
+ return groupTypes[group.type](group, options);
+ } else {
+ throw new ParseError(
+ "Got group of unknown type: '" + group.type + "'");
+ }
+};
+
+/**
+ * Takes a full parse tree and settings and builds a MathML representation of
+ * it. In particular, we put the elements from building the parse tree into a
+ * <semantics> tag so we can also include that TeX source as an annotation.
+ *
+ * Note that we actually return a domTree element with a `<math>` inside it so
+ * we can do appropriate styling.
+ */
+var buildMathML = function(tree, texExpression, options) {
+ var expression = buildExpression(tree, options);
+
+ // Wrap up the expression in an mrow so it is presented in the semantics
+ // tag correctly.
+ var wrapper = new mathMLTree.MathNode("mrow", expression);
+
+ // Build a TeX annotation of the source
+ var annotation = new mathMLTree.MathNode(
+ "annotation", [new mathMLTree.TextNode(texExpression)]);
+
+ annotation.setAttribute("encoding", "application/x-tex");
+
+ var semantics = new mathMLTree.MathNode(
+ "semantics", [wrapper, annotation]);
+
+ var math = new mathMLTree.MathNode("math", [semantics]);
+
+ // You can't style <math> nodes, so we wrap the node in a span.
+ return makeSpan(["katex-mathml"], [math]);
+};
+
+module.exports = buildMathML;
+
+},{"./ParseError":6,"./buildCommon":10,"./fontMetrics":17,"./mathMLTree":20,"./symbols":23,"./utils":25}],13:[function(require,module,exports){
+var buildHTML = require("./buildHTML");
+var buildMathML = require("./buildMathML");
+var buildCommon = require("./buildCommon");
+var Options = require("./Options");
+var Settings = require("./Settings");
+var Style = require("./Style");
+
+var makeSpan = buildCommon.makeSpan;
+
+var buildTree = function(tree, expression, settings) {
+ settings = settings || new Settings({});
+
+ var startStyle = Style.TEXT;
+ if (settings.displayMode) {
+ startStyle = Style.DISPLAY;
+ }
+
+ // Setup the default options
+ var options = new Options({
+ style: startStyle,
+ size: "size5",
+ });
+
+ // `buildHTML` sometimes messes with the parse tree (like turning bins ->
+ // ords), so we build the MathML version first.
+ var mathMLNode = buildMathML(tree, expression, options);
+ var htmlNode = buildHTML(tree, options);
+
+ var katexNode = makeSpan(["katex"], [
+ mathMLNode, htmlNode,
+ ]);
+
+ if (settings.displayMode) {
+ return makeSpan(["katex-display"], [katexNode]);
+ } else {
+ return katexNode;
+ }
+};
+
+module.exports = buildTree;
+
+},{"./Options":5,"./Settings":8,"./Style":9,"./buildCommon":10,"./buildHTML":11,"./buildMathML":12}],14:[function(require,module,exports){
+/**
+ * This file deals with creating delimiters of various sizes. The TeXbook
+ * discusses these routines on page 441-442, in the "Another subroutine sets box
+ * x to a specified variable delimiter" paragraph.
+ *
+ * There are three main routines here. `makeSmallDelim` makes a delimiter in the
+ * normal font, but in either text, script, or scriptscript style.
+ * `makeLargeDelim` makes a delimiter in textstyle, but in one of the Size1,
+ * Size2, Size3, or Size4 fonts. `makeStackedDelim` makes a delimiter out of
+ * smaller pieces that are stacked on top of one another.
+ *
+ * The functions take a parameter `center`, which determines if the delimiter
+ * should be centered around the axis.
+ *
+ * Then, there are three exposed functions. `sizedDelim` makes a delimiter in
+ * one of the given sizes. This is used for things like `\bigl`.
+ * `customSizedDelim` makes a delimiter with a given total height+depth. It is
+ * called in places like `\sqrt`. `leftRightDelim` makes an appropriate
+ * delimiter which surrounds an expression of a given height an depth. It is
+ * used in `\left` and `\right`.
+ */
+
+var ParseError = require("./ParseError");
+var Style = require("./Style");
+
+var buildCommon = require("./buildCommon");
+var fontMetrics = require("./fontMetrics");
+var symbols = require("./symbols");
+var utils = require("./utils");
+
+var makeSpan = buildCommon.makeSpan;
+
+/**
+ * Get the metrics for a given symbol and font, after transformation (i.e.
+ * after following replacement from symbols.js)
+ */
+var getMetrics = function(symbol, font) {
+ if (symbols.math[symbol] && symbols.math[symbol].replace) {
+ return fontMetrics.getCharacterMetrics(
+ symbols.math[symbol].replace, font);
+ } else {
+ return fontMetrics.getCharacterMetrics(
+ symbol, font);
+ }
+};
+
+/**
+ * Builds a symbol in the given font size (note size is an integer)
+ */
+var mathrmSize = function(value, size, mode) {
+ return buildCommon.makeSymbol(value, "Size" + size + "-Regular", mode);
+};
+
+/**
+ * Puts a delimiter span in a given style, and adds appropriate height, depth,
+ * and maxFontSizes.
+ */
+var styleWrap = function(delim, toStyle, options) {
+ var span = makeSpan(
+ ["style-wrap", options.style.reset(), toStyle.cls()], [delim]);
+
+ var multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier;
+
+ span.height *= multiplier;
+ span.depth *= multiplier;
+ span.maxFontSize = toStyle.sizeMultiplier;
+
+ return span;
+};
+
+/**
+ * Makes a small delimiter. This is a delimiter that comes in the Main-Regular
+ * font, but is restyled to either be in textstyle, scriptstyle, or
+ * scriptscriptstyle.
+ */
+var makeSmallDelim = function(delim, style, center, options, mode) {
+ var text = buildCommon.makeSymbol(delim, "Main-Regular", mode);
+
+ var span = styleWrap(text, style, options);
+
+ if (center) {
+ var shift =
+ (1 - options.style.sizeMultiplier / style.sizeMultiplier) *
+ fontMetrics.metrics.axisHeight;
+
+ span.style.top = shift + "em";
+ span.height -= shift;
+ span.depth += shift;
+ }
+
+ return span;
+};
+
+/**
+ * Makes a large delimiter. This is a delimiter that comes in the Size1, Size2,
+ * Size3, or Size4 fonts. It is always rendered in textstyle.
+ */
+var makeLargeDelim = function(delim, size, center, options, mode) {
+ var inner = mathrmSize(delim, size, mode);
+
+ var span = styleWrap(
+ makeSpan(["delimsizing", "size" + size],
+ [inner], options.getColor()),
+ Style.TEXT, options);
+
+ if (center) {
+ var shift = (1 - options.style.sizeMultiplier) *
+ fontMetrics.metrics.axisHeight;
+
+ span.style.top = shift + "em";
+ span.height -= shift;
+ span.depth += shift;
+ }
+
+ return span;
+};
+
+/**
+ * Make an inner span with the given offset and in the given font. This is used
+ * in `makeStackedDelim` to make the stacking pieces for the delimiter.
+ */
+var makeInner = function(symbol, font, mode) {
+ var sizeClass;
+ // Apply the correct CSS class to choose the right font.
+ if (font === "Size1-Regular") {
+ sizeClass = "delim-size1";
+ } else if (font === "Size4-Regular") {
+ sizeClass = "delim-size4";
+ }
+
+ var inner = makeSpan(
+ ["delimsizinginner", sizeClass],
+ [makeSpan([], [buildCommon.makeSymbol(symbol, font, mode)])]);
+
+ // Since this will be passed into `makeVList` in the end, wrap the element
+ // in the appropriate tag that VList uses.
+ return {type: "elem", elem: inner};
+};
+
+/**
+ * Make a stacked delimiter out of a given delimiter, with the total height at
+ * least `heightTotal`. This routine is mentioned on page 442 of the TeXbook.
+ */
+var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
+ // There are four parts, the top, an optional middle, a repeated part, and a
+ // bottom.
+ var top;
+ var middle;
+ var repeat;
+ var bottom;
+ top = repeat = bottom = delim;
+ middle = null;
+ // Also keep track of what font the delimiters are in
+ var font = "Size1-Regular";
+
+ // We set the parts and font based on the symbol. Note that we use
+ // '\u23d0' instead of '|' and '\u2016' instead of '\\|' for the
+ // repeats of the arrows
+ if (delim === "\\uparrow") {
+ repeat = bottom = "\u23d0";
+ } else if (delim === "\\Uparrow") {
+ repeat = bottom = "\u2016";
+ } else if (delim === "\\downarrow") {
+ top = repeat = "\u23d0";
+ } else if (delim === "\\Downarrow") {
+ top = repeat = "\u2016";
+ } else if (delim === "\\updownarrow") {
+ top = "\\uparrow";
+ repeat = "\u23d0";
+ bottom = "\\downarrow";
+ } else if (delim === "\\Updownarrow") {
+ top = "\\Uparrow";
+ repeat = "\u2016";
+ bottom = "\\Downarrow";
+ } else if (delim === "[" || delim === "\\lbrack") {
+ top = "\u23a1";
+ repeat = "\u23a2";
+ bottom = "\u23a3";
+ font = "Size4-Regular";
+ } else if (delim === "]" || delim === "\\rbrack") {
+ top = "\u23a4";
+ repeat = "\u23a5";
+ bottom = "\u23a6";
+ font = "Size4-Regular";
+ } else if (delim === "\\lfloor") {
+ repeat = top = "\u23a2";
+ bottom = "\u23a3";
+ font = "Size4-Regular";
+ } else if (delim === "\\lceil") {
+ top = "\u23a1";
+ repeat = bottom = "\u23a2";
+ font = "Size4-Regular";
+ } else if (delim === "\\rfloor") {
+ repeat = top = "\u23a5";
+ bottom = "\u23a6";
+ font = "Size4-Regular";
+ } else if (delim === "\\rceil") {
+ top = "\u23a4";
+ repeat = bottom = "\u23a5";
+ font = "Size4-Regular";
+ } else if (delim === "(") {
+ top = "\u239b";
+ repeat = "\u239c";
+ bottom = "\u239d";
+ font = "Size4-Regular";
+ } else if (delim === ")") {
+ top = "\u239e";
+ repeat = "\u239f";
+ bottom = "\u23a0";
+ font = "Size4-Regular";
+ } else if (delim === "\\{" || delim === "\\lbrace") {
+ top = "\u23a7";
+ middle = "\u23a8";
+ bottom = "\u23a9";
+ repeat = "\u23aa";
+ font = "Size4-Regular";
+ } else if (delim === "\\}" || delim === "\\rbrace") {
+ top = "\u23ab";
+ middle = "\u23ac";
+ bottom = "\u23ad";
+ repeat = "\u23aa";
+ font = "Size4-Regular";
+ } else if (delim === "\\lgroup") {
+ top = "\u23a7";
+ bottom = "\u23a9";
+ repeat = "\u23aa";
+ font = "Size4-Regular";
+ } else if (delim === "\\rgroup") {
+ top = "\u23ab";
+ bottom = "\u23ad";
+ repeat = "\u23aa";
+ font = "Size4-Regular";
+ } else if (delim === "\\lmoustache") {
+ top = "\u23a7";
+ bottom = "\u23ad";
+ repeat = "\u23aa";
+ font = "Size4-Regular";
+ } else if (delim === "\\rmoustache") {
+ top = "\u23ab";
+ bottom = "\u23a9";
+ repeat = "\u23aa";
+ font = "Size4-Regular";
+ } else if (delim === "\\surd") {
+ top = "\ue001";
+ bottom = "\u23b7";
+ repeat = "\ue000";
+ font = "Size4-Regular";
+ }
+
+ // Get the metrics of the four sections
+ var topMetrics = getMetrics(top, font);
+ var topHeightTotal = topMetrics.height + topMetrics.depth;
+ var repeatMetrics = getMetrics(repeat, font);
+ var repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth;
+ var bottomMetrics = getMetrics(bottom, font);
+ var bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth;
+ var middleHeightTotal = 0;
+ var middleFactor = 1;
+ if (middle !== null) {
+ var middleMetrics = getMetrics(middle, font);
+ middleHeightTotal = middleMetrics.height + middleMetrics.depth;
+ middleFactor = 2; // repeat symmetrically above and below middle
+ }
+
+ // Calcuate the minimal height that the delimiter can have.
+ // It is at least the size of the top, bottom, and optional middle combined.
+ var minHeight = topHeightTotal + bottomHeightTotal + middleHeightTotal;
+
+ // Compute the number of copies of the repeat symbol we will need
+ var repeatCount = Math.ceil(
+ (heightTotal - minHeight) / (middleFactor * repeatHeightTotal));
+
+ // Compute the total height of the delimiter including all the symbols
+ var realHeightTotal =
+ minHeight + repeatCount * middleFactor * repeatHeightTotal;
+
+ // The center of the delimiter is placed at the center of the axis. Note
+ // that in this context, "center" means that the delimiter should be
+ // centered around the axis in the current style, while normally it is
+ // centered around the axis in textstyle.
+ var axisHeight = fontMetrics.metrics.axisHeight;
+ if (center) {
+ axisHeight *= options.style.sizeMultiplier;
+ }
+ // Calculate the depth
+ var depth = realHeightTotal / 2 - axisHeight;
+
+ // Now, we start building the pieces that will go into the vlist
+
+ // Keep a list of the inner pieces
+ var inners = [];
+
+ // Add the bottom symbol
+ inners.push(makeInner(bottom, font, mode));
+
+ var i;
+ if (middle === null) {
+ // Add that many symbols
+ for (i = 0; i < repeatCount; i++) {
+ inners.push(makeInner(repeat, font, mode));
+ }
+ } else {
+ // When there is a middle bit, we need the middle part and two repeated
+ // sections
+ for (i = 0; i < repeatCount; i++) {
+ inners.push(makeInner(repeat, font, mode));
+ }
+ inners.push(makeInner(middle, font, mode));
+ for (i = 0; i < repeatCount; i++) {
+ inners.push(makeInner(repeat, font, mode));
+ }
+ }
+
+ // Add the top symbol
+ inners.push(makeInner(top, font, mode));
+
+ // Finally, build the vlist
+ var inner = buildCommon.makeVList(inners, "bottom", depth, options);
+
+ return styleWrap(
+ makeSpan(["delimsizing", "mult"], [inner], options.getColor()),
+ Style.TEXT, options);
+};
+
+// There are three kinds of delimiters, delimiters that stack when they become
+// too large
+var stackLargeDelimiters = [
+ "(", ")", "[", "\\lbrack", "]", "\\rbrack",
+ "\\{", "\\lbrace", "\\}", "\\rbrace",
+ "\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
+ "\\surd",
+];
+
+// delimiters that always stack
+var stackAlwaysDelimiters = [
+ "\\uparrow", "\\downarrow", "\\updownarrow",
+ "\\Uparrow", "\\Downarrow", "\\Updownarrow",
+ "|", "\\|", "\\vert", "\\Vert",
+ "\\lvert", "\\rvert", "\\lVert", "\\rVert",
+ "\\lgroup", "\\rgroup", "\\lmoustache", "\\rmoustache",
+];
+
+// and delimiters that never stack
+var stackNeverDelimiters = [
+ "<", ">", "\\langle", "\\rangle", "/", "\\backslash", "\\lt", "\\gt",
+];
+
+// Metrics of the different sizes. Found by looking at TeX's output of
+// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
+// Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
+var sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
+
+/**
+ * Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4.
+ */
+var makeSizedDelim = function(delim, size, options, mode) {
+ // < and > turn into \langle and \rangle in delimiters
+ if (delim === "<" || delim === "\\lt") {
+ delim = "\\langle";
+ } else if (delim === ">" || delim === "\\gt") {
+ delim = "\\rangle";
+ }
+
+ // Sized delimiters are never centered.
+ if (utils.contains(stackLargeDelimiters, delim) ||
+ utils.contains(stackNeverDelimiters, delim)) {
+ return makeLargeDelim(delim, size, false, options, mode);
+ } else if (utils.contains(stackAlwaysDelimiters, delim)) {
+ return makeStackedDelim(
+ delim, sizeToMaxHeight[size], false, options, mode);
+ } else {
+ throw new ParseError("Illegal delimiter: '" + delim + "'");
+ }
+};
+
+/**
+ * There are three different sequences of delimiter sizes that the delimiters
+ * follow depending on the kind of delimiter. This is used when creating custom
+ * sized delimiters to decide whether to create a small, large, or stacked
+ * delimiter.
+ *
+ * In real TeX, these sequences aren't explicitly defined, but are instead
+ * defined inside the font metrics. Since there are only three sequences that
+ * are possible for the delimiters that TeX defines, it is easier to just encode
+ * them explicitly here.
+ */
+
+// Delimiters that never stack try small delimiters and large delimiters only
+var stackNeverDelimiterSequence = [
+ {type: "small", style: Style.SCRIPTSCRIPT},
+ {type: "small", style: Style.SCRIPT},
+ {type: "small", style: Style.TEXT},
+ {type: "large", size: 1},
+ {type: "large", size: 2},
+ {type: "large", size: 3},
+ {type: "large", size: 4},
+];
+
+// Delimiters that always stack try the small delimiters first, then stack
+var stackAlwaysDelimiterSequence = [
+ {type: "small", style: Style.SCRIPTSCRIPT},
+ {type: "small", style: Style.SCRIPT},
+ {type: "small", style: Style.TEXT},
+ {type: "stack"},
+];
+
+// Delimiters that stack when large try the small and then large delimiters, and
+// stack afterwards
+var stackLargeDelimiterSequence = [
+ {type: "small", style: Style.SCRIPTSCRIPT},
+ {type: "small", style: Style.SCRIPT},
+ {type: "small", style: Style.TEXT},
+ {type: "large", size: 1},
+ {type: "large", size: 2},
+ {type: "large", size: 3},
+ {type: "large", size: 4},
+ {type: "stack"},
+];
+
+/**
+ * Get the font used in a delimiter based on what kind of delimiter it is.
+ */
+var delimTypeToFont = function(type) {
+ if (type.type === "small") {
+ return "Main-Regular";
+ } else if (type.type === "large") {
+ return "Size" + type.size + "-Regular";
+ } else if (type.type === "stack") {
+ return "Size4-Regular";
+ }
+};
+
+/**
+ * Traverse a sequence of types of delimiters to decide what kind of delimiter
+ * should be used to create a delimiter of the given height+depth.
+ */
+var traverseSequence = function(delim, height, sequence, options) {
+ // Here, we choose the index we should start at in the sequences. In smaller
+ // sizes (which correspond to larger numbers in style.size) we start earlier
+ // in the sequence. Thus, scriptscript starts at index 3-3=0, script starts
+ // at index 3-2=1, text starts at 3-1=2, and display starts at min(2,3-0)=2
+ var start = Math.min(2, 3 - options.style.size);
+ for (var i = start; i < sequence.length; i++) {
+ if (sequence[i].type === "stack") {
+ // This is always the last delimiter, so we just break the loop now.
+ break;
+ }
+
+ var metrics = getMetrics(delim, delimTypeToFont(sequence[i]));
+ var heightDepth = metrics.height + metrics.depth;
+
+ // Small delimiters are scaled down versions of the same font, so we
+ // account for the style change size.
+
+ if (sequence[i].type === "small") {
+ heightDepth *= sequence[i].style.sizeMultiplier;
+ }
+
+ // Check if the delimiter at this size works for the given height.
+ if (heightDepth > height) {
+ return sequence[i];
+ }
+ }
+
+ // If we reached the end of the sequence, return the last sequence element.
+ return sequence[sequence.length - 1];
+};
+
+/**
+ * Make a delimiter of a given height+depth, with optional centering. Here, we
+ * traverse the sequences, and create a delimiter that the sequence tells us to.
+ */
+var makeCustomSizedDelim = function(delim, height, center, options, mode) {
+ if (delim === "<" || delim === "\\lt") {
+ delim = "\\langle";
+ } else if (delim === ">" || delim === "\\gt") {
+ delim = "\\rangle";
+ }
+
+ // Decide what sequence to use
+ var sequence;
+ if (utils.contains(stackNeverDelimiters, delim)) {
+ sequence = stackNeverDelimiterSequence;
+ } else if (utils.contains(stackLargeDelimiters, delim)) {
+ sequence = stackLargeDelimiterSequence;
+ } else {
+ sequence = stackAlwaysDelimiterSequence;
+ }
+
+ // Look through the sequence
+ var delimType = traverseSequence(delim, height, sequence, options);
+
+ // Depending on the sequence element we decided on, call the appropriate
+ // function.
+ if (delimType.type === "small") {
+ return makeSmallDelim(delim, delimType.style, center, options, mode);
+ } else if (delimType.type === "large") {
+ return makeLargeDelim(delim, delimType.size, center, options, mode);
+ } else if (delimType.type === "stack") {
+ return makeStackedDelim(delim, height, center, options, mode);
+ }
+};
+
+/**
+ * Make a delimiter for use with `\left` and `\right`, given a height and depth
+ * of an expression that the delimiters surround.
+ */
+var makeLeftRightDelim = function(delim, height, depth, options, mode) {
+ // We always center \left/\right delimiters, so the axis is always shifted
+ var axisHeight =
+ fontMetrics.metrics.axisHeight * options.style.sizeMultiplier;
+
+ // Taken from TeX source, tex.web, function make_left_right
+ var delimiterFactor = 901;
+ var delimiterExtend = 5.0 / fontMetrics.metrics.ptPerEm;
+
+ var maxDistFromAxis = Math.max(
+ height - axisHeight, depth + axisHeight);
+
+ var totalHeight = Math.max(
+ // In real TeX, calculations are done using integral values which are
+ // 65536 per pt, or 655360 per em. So, the division here truncates in
+ // TeX but doesn't here, producing different results. If we wanted to
+ // exactly match TeX's calculation, we could do
+ // Math.floor(655360 * maxDistFromAxis / 500) *
+ // delimiterFactor / 655360
+ // (To see the difference, compare
+ // x^{x^{\left(\rule{0.1em}{0.68em}\right)}}
+ // in TeX and KaTeX)
+ maxDistFromAxis / 500 * delimiterFactor,
+ 2 * maxDistFromAxis - delimiterExtend);
+
+ // Finally, we defer to `makeCustomSizedDelim` with our calculated total
+ // height
+ return makeCustomSizedDelim(delim, totalHeight, true, options, mode);
+};
+
+module.exports = {
+ sizedDelim: makeSizedDelim,
+ customSizedDelim: makeCustomSizedDelim,
+ leftRightDelim: makeLeftRightDelim,
+};
+
+},{"./ParseError":6,"./Style":9,"./buildCommon":10,"./fontMetrics":17,"./symbols":23,"./utils":25}],15:[function(require,module,exports){
+/**
+ * These objects store the data about the DOM nodes we create, as well as some
+ * extra data. They can then be transformed into real DOM nodes with the
+ * `toNode` function or HTML markup using `toMarkup`. They are useful for both
+ * storing extra properties on the nodes, as well as providing a way to easily
+ * work with the DOM.
+ *
+ * Similar functions for working with MathML nodes exist in mathMLTree.js.
+ */
+var unicodeRegexes = require("./unicodeRegexes");
+var utils = require("./utils");
+
+/**
+ * Create an HTML className based on a list of classes. In addition to joining
+ * with spaces, we also remove null or empty classes.
+ */
+var createClass = function(classes) {
+ classes = classes.slice();
+ for (var i = classes.length - 1; i >= 0; i--) {
+ if (!classes[i]) {
+ classes.splice(i, 1);
+ }
+ }
+
+ return classes.join(" ");
+};
+
+/**
+ * This node represents a span node, with a className, a list of children, and
+ * an inline style. It also contains information about its height, depth, and
+ * maxFontSize.
+ */
+function span(classes, children, height, depth, maxFontSize, style) {
+ this.classes = classes || [];
+ this.children = children || [];
+ this.height = height || 0;
+ this.depth = depth || 0;
+ this.maxFontSize = maxFontSize || 0;
+ this.style = style || {};
+ this.attributes = {};
+}
+
+/**
+ * Sets an arbitrary attribute on the span. Warning: use this wisely. Not all
+ * browsers support attributes the same, and having too many custom attributes
+ * is probably bad.
+ */
+span.prototype.setAttribute = function(attribute, value) {
+ this.attributes[attribute] = value;
+};
+
+/**
+ * Convert the span into an HTML node
+ */
+span.prototype.toNode = function() {
+ var span = document.createElement("span");
+
+ // Apply the class
+ span.className = createClass(this.classes);
+
+ // Apply inline styles
+ for (var style in this.style) {
+ if (Object.prototype.hasOwnProperty.call(this.style, style)) {
+ span.style[style] = this.style[style];
+ }
+ }
+
+ // Apply attributes
+ for (var attr in this.attributes) {
+ if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
+ span.setAttribute(attr, this.attributes[attr]);
+ }
+ }
+
+ // Append the children, also as HTML nodes
+ for (var i = 0; i < this.children.length; i++) {
+ span.appendChild(this.children[i].toNode());
+ }
+
+ return span;
+};
+
+/**
+ * Convert the span into an HTML markup string
+ */
+span.prototype.toMarkup = function() {
+ var markup = "<span";
+
+ // Add the class
+ if (this.classes.length) {
+ markup += " class=\"";
+ markup += utils.escape(createClass(this.classes));
+ markup += "\"";
+ }
+
+ var styles = "";
+
+ // Add the styles, after hyphenation
+ for (var style in this.style) {
+ if (this.style.hasOwnProperty(style)) {
+ styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
+ }
+ }
+
+ if (styles) {
+ markup += " style=\"" + utils.escape(styles) + "\"";
+ }
+
+ // Add the attributes
+ for (var attr in this.attributes) {
+ if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
+ markup += " " + attr + "=\"";
+ markup += utils.escape(this.attributes[attr]);
+ markup += "\"";
+ }
+ }
+
+ markup += ">";
+
+ // Add the markup of the children, also as markup
+ for (var i = 0; i < this.children.length; i++) {
+ markup += this.children[i].toMarkup();
+ }
+
+ markup += "</span>";
+
+ return markup;
+};
+
+/**
+ * This node represents a document fragment, which contains elements, but when
+ * placed into the DOM doesn't have any representation itself. Thus, it only
+ * contains children and doesn't have any HTML properties. It also keeps track
+ * of a height, depth, and maxFontSize.
+ */
+function documentFragment(children, height, depth, maxFontSize) {
+ this.children = children || [];
+ this.height = height || 0;
+ this.depth = depth || 0;
+ this.maxFontSize = maxFontSize || 0;
+}
+
+/**
+ * Convert the fragment into a node
+ */
+documentFragment.prototype.toNode = function() {
+ // Create a fragment
+ var frag = document.createDocumentFragment();
+
+ // Append the children
+ for (var i = 0; i < this.children.length; i++) {
+ frag.appendChild(this.children[i].toNode());
+ }
+
+ return frag;
+};
+
+/**
+ * Convert the fragment into HTML markup
+ */
+documentFragment.prototype.toMarkup = function() {
+ var markup = "";
+
+ // Simply concatenate the markup for the children together
+ for (var i = 0; i < this.children.length; i++) {
+ markup += this.children[i].toMarkup();
+ }
+
+ return markup;
+};
+
+var iCombinations = {
+ 'î': '\u0131\u0302',
+ 'ï': '\u0131\u0308',
+ 'í': '\u0131\u0301',
+ // 'ī': '\u0131\u0304', // enable when we add Extended Latin
+ 'ì': '\u0131\u0300',
+};
+
+/**
+ * A symbol node contains information about a single symbol. It either renders
+ * to a single text node, or a span with a single text node in it, depending on
+ * whether it has CSS classes, styles, or needs italic correction.
+ */
+function symbolNode(value, height, depth, italic, skew, classes, style) {
+ this.value = value || "";
+ this.height = height || 0;
+ this.depth = depth || 0;
+ this.italic = italic || 0;
+ this.skew = skew || 0;
+ this.classes = classes || [];
+ this.style = style || {};
+ this.maxFontSize = 0;
+
+ // Mark CJK characters with specific classes so that we can specify which
+ // fonts to use. This allows us to render these characters with a serif
+ // font in situations where the browser would either default to a sans serif
+ // or render a placeholder character.
+ if (unicodeRegexes.cjkRegex.test(value)) {
+ // I couldn't find any fonts that contained Hangul as well as all of
+ // the other characters we wanted to test there for it gets its own
+ // CSS class.
+ if (unicodeRegexes.hangulRegex.test(value)) {
+ this.classes.push('hangul_fallback');
+ } else {
+ this.classes.push('cjk_fallback');
+ }
+ }
+
+ if (/[îïíì]/.test(this.value)) { // add ī when we add Extended Latin
+ this.value = iCombinations[this.value];
+ }
+}
+
+/**
+ * Creates a text node or span from a symbol node. Note that a span is only
+ * created if it is needed.
+ */
+symbolNode.prototype.toNode = function() {
+ var node = document.createTextNode(this.value);
+ var span = null;
+
+ if (this.italic > 0) {
+ span = document.createElement("span");
+ span.style.marginRight = this.italic + "em";
+ }
+
+ if (this.classes.length > 0) {
+ span = span || document.createElement("span");
+ span.className = createClass(this.classes);
+ }
+
+ for (var style in this.style) {
+ if (this.style.hasOwnProperty(style)) {
+ span = span || document.createElement("span");
+ span.style[style] = this.style[style];
+ }
+ }
+
+ if (span) {
+ span.appendChild(node);
+ return span;
+ } else {
+ return node;
+ }
+};
+
+/**
+ * Creates markup for a symbol node.
+ */
+symbolNode.prototype.toMarkup = function() {
+ // TODO(alpert): More duplication than I'd like from
+ // span.prototype.toMarkup and symbolNode.prototype.toNode...
+ var needsSpan = false;
+
+ var markup = "<span";
+
+ if (this.classes.length) {
+ needsSpan = true;
+ markup += " class=\"";
+ markup += utils.escape(createClass(this.classes));
+ markup += "\"";
+ }
+
+ var styles = "";
+
+ if (this.italic > 0) {
+ styles += "margin-right:" + this.italic + "em;";
+ }
+ for (var style in this.style) {
+ if (this.style.hasOwnProperty(style)) {
+ styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
+ }
+ }
+
+ if (styles) {
+ needsSpan = true;
+ markup += " style=\"" + utils.escape(styles) + "\"";
+ }
+
+ var escaped = utils.escape(this.value);
+ if (needsSpan) {
+ markup += ">";
+ markup += escaped;
+ markup += "</span>";
+ return markup;
+ } else {
+ return escaped;
+ }
+};
+
+module.exports = {
+ span: span,
+ documentFragment: documentFragment,
+ symbolNode: symbolNode,
+};
+
+},{"./unicodeRegexes":24,"./utils":25}],16:[function(require,module,exports){
+/* eslint no-constant-condition:0 */
+var fontMetrics = require("./fontMetrics");
+var parseData = require("./parseData");
+var ParseError = require("./ParseError");
+
+var ParseNode = parseData.ParseNode;
+
+/**
+ * Parse the body of the environment, with rows delimited by \\ and
+ * columns delimited by &, and create a nested list in row-major order
+ * with one group per cell.
+ */
+function parseArray(parser, result) {
+ var row = [];
+ var body = [row];
+ var rowGaps = [];
+ while (true) {
+ var cell = parser.parseExpression(false, null);
+ row.push(new ParseNode("ordgroup", cell, parser.mode));
+ var next = parser.nextToken.text;
+ if (next === "&") {
+ parser.consume();
+ } else if (next === "\\end") {
+ break;
+ } else if (next === "\\\\" || next === "\\cr") {
+ var cr = parser.parseFunction();
+ rowGaps.push(cr.value.size);
+ row = [];
+ body.push(row);
+ } else {
+ throw new ParseError("Expected & or \\\\ or \\end",
+ parser.nextToken);
+ }
+ }
+ result.body = body;
+ result.rowGaps = rowGaps;
+ return new ParseNode(result.type, result, parser.mode);
+}
+
+/*
+ * An environment definition is very similar to a function definition:
+ * it is declared with a name or a list of names, a set of properties
+ * and a handler containing the actual implementation.
+ *
+ * The properties include:
+ * - numArgs: The number of arguments after the \begin{name} function.
+ * - argTypes: (optional) Just like for a function
+ * - allowedInText: (optional) Whether or not the environment is allowed inside
+ * text mode (default false) (not enforced yet)
+ * - numOptionalArgs: (optional) Just like for a function
+ * A bare number instead of that object indicates the numArgs value.
+ *
+ * The handler function will receive two arguments
+ * - context: information and references provided by the parser
+ * - args: an array of arguments passed to \begin{name}
+ * The context contains the following properties:
+ * - envName: the name of the environment, one of the listed names.
+ * - parser: the parser object
+ * - lexer: the lexer object
+ * - positions: the positions associated with these arguments from args.
+ * The handler must return a ParseResult.
+ */
+
+function defineEnvironment(names, props, handler) {
+ if (typeof names === "string") {
+ names = [names];
+ }
+ if (typeof props === "number") {
+ props = { numArgs: props };
+ }
+ // Set default values of environments
+ var data = {
+ numArgs: props.numArgs || 0,
+ argTypes: props.argTypes,
+ greediness: 1,
+ allowedInText: !!props.allowedInText,
+ numOptionalArgs: props.numOptionalArgs || 0,
+ handler: handler,
+ };
+ for (var i = 0; i < names.length; ++i) {
+ module.exports[names[i]] = data;
+ }
+}
+
+// Arrays are part of LaTeX, defined in lttab.dtx so its documentation
+// is part of the source2e.pdf file of LaTeX2e source documentation.
+defineEnvironment("array", {
+ numArgs: 1,
+}, function(context, args) {
+ var colalign = args[0];
+ colalign = colalign.value.map ? colalign.value : [colalign];
+ var cols = colalign.map(function(node) {
+ var ca = node.value;
+ if ("lcr".indexOf(ca) !== -1) {
+ return {
+ type: "align",
+ align: ca,
+ };
+ } else if (ca === "|") {
+ return {
+ type: "separator",
+ separator: "|",
+ };
+ }
+ throw new ParseError(
+ "Unknown column alignment: " + node.value,
+ node);
+ });
+ var res = {
+ type: "array",
+ cols: cols,
+ hskipBeforeAndAfter: true, // \@preamble in lttab.dtx
+ };
+ res = parseArray(context.parser, res);
+ return res;
+});
+
+// The matrix environments of amsmath builds on the array environment
+// of LaTeX, which is discussed above.
+defineEnvironment([
+ "matrix",
+ "pmatrix",
+ "bmatrix",
+ "Bmatrix",
+ "vmatrix",
+ "Vmatrix",
+], {
+}, function(context) {
+ var delimiters = {
+ "matrix": null,
+ "pmatrix": ["(", ")"],
+ "bmatrix": ["[", "]"],
+ "Bmatrix": ["\\{", "\\}"],
+ "vmatrix": ["|", "|"],
+ "Vmatrix": ["\\Vert", "\\Vert"],
+ }[context.envName];
+ var res = {
+ type: "array",
+ hskipBeforeAndAfter: false, // \hskip -\arraycolsep in amsmath
+ };
+ res = parseArray(context.parser, res);
+ if (delimiters) {
+ res = new ParseNode("leftright", {
+ body: [res],
+ left: delimiters[0],
+ right: delimiters[1],
+ }, context.mode);
+ }
+ return res;
+});
+
+// A cases environment (in amsmath.sty) is almost equivalent to
+// \def\arraystretch{1.2}%
+// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right.
+defineEnvironment("cases", {
+}, function(context) {
+ var res = {
+ type: "array",
+ arraystretch: 1.2,
+ cols: [{
+ type: "align",
+ align: "l",
+ pregap: 0,
+ postgap: fontMetrics.metrics.quad,
+ }, {
+ type: "align",
+ align: "l",
+ pregap: 0,
+ postgap: 0,
+ }],
+ };
+ res = parseArray(context.parser, res);
+ res = new ParseNode("leftright", {
+ body: [res],
+ left: "\\{",
+ right: ".",
+ }, context.mode);
+ return res;
+});
+
+// An aligned environment is like the align* environment
+// except it operates within math mode.
+// Note that we assume \nomallineskiplimit to be zero,
+// so that \strut@ is the same as \strut.
+defineEnvironment("aligned", {
+}, function(context) {
+ var res = {
+ type: "array",
+ cols: [],
+ };
+ res = parseArray(context.parser, res);
+ var emptyGroup = new ParseNode("ordgroup", [], context.mode);
+ var numCols = 0;
+ res.value.body.forEach(function(row) {
+ var i;
+ for (i = 1; i < row.length; i += 2) {
+ row[i].value.unshift(emptyGroup);
+ }
+ if (numCols < row.length) {
+ numCols = row.length;
+ }
+ });
+ for (var i = 0; i < numCols; ++i) {
+ var align = "r";
+ var pregap = 0;
+ if (i % 2 === 1) {
+ align = "l";
+ } else if (i > 0) {
+ pregap = 2; // one \qquad between columns
+ }
+ res.value.cols[i] = {
+ type: "align",
+ align: align,
+ pregap: pregap,
+ postgap: 0,
+ };
+ }
+ return res;
+});
+
+},{"./ParseError":6,"./fontMetrics":17,"./parseData":21}],17:[function(require,module,exports){
+/* eslint no-unused-vars:0 */
+
+var Style = require("./Style");
+var cjkRegex = require("./unicodeRegexes").cjkRegex;
+
+/**
+ * This file contains metrics regarding fonts and individual symbols. The sigma
+ * and xi variables, as well as the metricMap map contain data extracted from
+ * TeX, TeX font metrics, and the TTF files. These data are then exposed via the
+ * `metrics` variable and the getCharacterMetrics function.
+ */
+
+// These font metrics are extracted from TeX by using
+// \font\a=cmmi10
+// \showthe\fontdimenX\a
+// where X is the corresponding variable number. These correspond to the font
+// parameters of the symbol fonts. In TeX, there are actually three sets of
+// dimensions, one for each of textstyle, scriptstyle, and scriptscriptstyle,
+// but we only use the textstyle ones, and scale certain dimensions accordingly.
+// See the TeXbook, page 441.
+var sigma1 = 0.025;
+var sigma2 = 0;
+var sigma3 = 0;
+var sigma4 = 0;
+var sigma5 = 0.431;
+var sigma6 = 1;
+var sigma7 = 0;
+var sigma8 = 0.677;
+var sigma9 = 0.394;
+var sigma10 = 0.444;
+var sigma11 = 0.686;
+var sigma12 = 0.345;
+var sigma13 = 0.413;
+var sigma14 = 0.363;
+var sigma15 = 0.289;
+var sigma16 = 0.150;
+var sigma17 = 0.247;
+var sigma18 = 0.386;
+var sigma19 = 0.050;
+var sigma20 = 2.390;
+var sigma21 = 1.01;
+var sigma21Script = 0.81;
+var sigma21ScriptScript = 0.71;
+var sigma22 = 0.250;
+
+// These font metrics are extracted from TeX by using
+// \font\a=cmex10
+// \showthe\fontdimenX\a
+// where X is the corresponding variable number. These correspond to the font
+// parameters of the extension fonts (family 3). See the TeXbook, page 441.
+var xi1 = 0;
+var xi2 = 0;
+var xi3 = 0;
+var xi4 = 0;
+var xi5 = 0.431;
+var xi6 = 1;
+var xi7 = 0;
+var xi8 = 0.04;
+var xi9 = 0.111;
+var xi10 = 0.166;
+var xi11 = 0.2;
+var xi12 = 0.6;
+var xi13 = 0.1;
+
+// This value determines how large a pt is, for metrics which are defined in
+// terms of pts.
+// This value is also used in katex.less; if you change it make sure the values
+// match.
+var ptPerEm = 10.0;
+
+// The space between adjacent `|` columns in an array definition. From
+// `\showthe\doublerulesep` in LaTeX.
+var doubleRuleSep = 2.0 / ptPerEm;
+
+/**
+ * This is just a mapping from common names to real metrics
+ */
+var metrics = {
+ xHeight: sigma5,
+ quad: sigma6,
+ num1: sigma8,
+ num2: sigma9,
+ num3: sigma10,
+ denom1: sigma11,
+ denom2: sigma12,
+ sup1: sigma13,
+ sup2: sigma14,
+ sup3: sigma15,
+ sub1: sigma16,
+ sub2: sigma17,
+ supDrop: sigma18,
+ subDrop: sigma19,
+ axisHeight: sigma22,
+ defaultRuleThickness: xi8,
+ bigOpSpacing1: xi9,
+ bigOpSpacing2: xi10,
+ bigOpSpacing3: xi11,
+ bigOpSpacing4: xi12,
+ bigOpSpacing5: xi13,
+ ptPerEm: ptPerEm,
+ emPerEx: sigma5 / sigma6,
+ doubleRuleSep: doubleRuleSep,
+
+ // TODO(alpert): Missing parallel structure here. We should probably add
+ // style-specific metrics for all of these.
+ delim1: sigma20,
+ getDelim2: function(style) {
+ if (style.size === Style.TEXT.size) {
+ return sigma21;
+ } else if (style.size === Style.SCRIPT.size) {
+ return sigma21Script;
+ } else if (style.size === Style.SCRIPTSCRIPT.size) {
+ return sigma21ScriptScript;
+ }
+ throw new Error("Unexpected style size: " + style.size);
+ },
+};
+
+// This map contains a mapping from font name and character code to character
+// metrics, including height, depth, italic correction, and skew (kern from the
+// character to the corresponding \skewchar)
+// This map is generated via `make metrics`. It should not be changed manually.
+var metricMap = require("./fontMetricsData");
+
+// These are very rough approximations. We default to Times New Roman which
+// should have Latin-1 and Cyrillic characters, but may not depending on the
+// operating system. The metrics do not account for extra height from the
+// accents. In the case of Cyrillic characters which have both ascenders and
+// descenders we prefer approximations with ascenders, primarily to prevent
+// the fraction bar or root line from intersecting the glyph.
+// TODO(kevinb) allow union of multiple glyph metrics for better accuracy.
+var extraCharacterMap = {
+ // Latin-1
+ 'À': 'A',
+ 'Á': 'A',
+ 'Â': 'A',
+ 'Ã': 'A',
+ 'Ä': 'A',
+ 'Å': 'A',
+ 'Æ': 'A',
+ 'Ç': 'C',
+ 'È': 'E',
+ 'É': 'E',
+ 'Ê': 'E',
+ 'Ë': 'E',
+ 'Ì': 'I',
+ 'Í': 'I',
+ 'Î': 'I',
+ 'Ï': 'I',
+ 'Ð': 'D',
+ 'Ñ': 'N',
+ 'Ò': 'O',
+ 'Ó': 'O',
+ 'Ô': 'O',
+ 'Õ': 'O',
+ 'Ö': 'O',
+ 'Ø': 'O',
+ 'Ù': 'U',
+ 'Ú': 'U',
+ 'Û': 'U',
+ 'Ü': 'U',
+ 'Ý': 'Y',
+ 'Þ': 'o',
+ 'ß': 'B',
+ 'à': 'a',
+ 'á': 'a',
+ 'â': 'a',
+ 'ã': 'a',
+ 'ä': 'a',
+ 'å': 'a',
+ 'æ': 'a',
+ 'ç': 'c',
+ 'è': 'e',
+ 'é': 'e',
+ 'ê': 'e',
+ 'ë': 'e',
+ 'ì': 'i',
+ 'í': 'i',
+ 'î': 'i',
+ 'ï': 'i',
+ 'ð': 'd',
+ 'ñ': 'n',
+ 'ò': 'o',
+ 'ó': 'o',
+ 'ô': 'o',
+ 'õ': 'o',
+ 'ö': 'o',
+ 'ø': 'o',
+ 'ù': 'u',
+ 'ú': 'u',
+ 'û': 'u',
+ 'ü': 'u',
+ 'ý': 'y',
+ 'þ': 'o',
+ 'ÿ': 'y',
+
+ // Cyrillic
+ 'А': 'A',
+ 'Б': 'B',
+ 'В': 'B',
+ 'Г': 'F',
+ 'Д': 'A',
+ 'Е': 'E',
+ 'Ж': 'K',
+ 'З': '3',
+ 'И': 'N',
+ 'Й': 'N',
+ 'К': 'K',
+ 'Л': 'N',
+ 'М': 'M',
+ 'Н': 'H',
+ 'О': 'O',
+ 'П': 'N',
+ 'Р': 'P',
+ 'С': 'C',
+ 'Т': 'T',
+ 'У': 'y',
+ 'Ф': 'O',
+ 'Х': 'X',
+ 'Ц': 'U',
+ 'Ч': 'h',
+ 'Ш': 'W',
+ 'Щ': 'W',
+ 'Ъ': 'B',
+ 'Ы': 'X',
+ 'Ь': 'B',
+ 'Э': '3',
+ 'Ю': 'X',
+ 'Я': 'R',
+ 'а': 'a',
+ 'б': 'b',
+ 'в': 'a',
+ 'г': 'r',
+ 'д': 'y',
+ 'е': 'e',
+ 'ж': 'm',
+ 'з': 'e',
+ 'и': 'n',
+ 'й': 'n',
+ 'к': 'n',
+ 'л': 'n',
+ 'м': 'm',
+ 'н': 'n',
+ 'о': 'o',
+ 'п': 'n',
+ 'р': 'p',
+ 'с': 'c',
+ 'т': 'o',
+ 'у': 'y',
+ 'ф': 'b',
+ 'х': 'x',
+ 'ц': 'n',
+ 'ч': 'n',
+ 'ш': 'w',
+ 'щ': 'w',
+ 'ъ': 'a',
+ 'ы': 'm',
+ 'ь': 'a',
+ 'э': 'e',
+ 'ю': 'm',
+ 'я': 'r',
+};
+
+/**
+ * This function is a convenience function for looking up information in the
+ * metricMap table. It takes a character as a string, and a style.
+ *
+ * Note: the `width` property may be undefined if fontMetricsData.js wasn't
+ * built using `Make extended_metrics`.
+ */
+var getCharacterMetrics = function(character, style) {
+ var ch = character.charCodeAt(0);
+ if (character[0] in extraCharacterMap) {
+ ch = extraCharacterMap[character[0]].charCodeAt(0);
+ } else if (cjkRegex.test(character[0])) {
+ ch = 'M'.charCodeAt(0);
+ }
+ var metrics = metricMap[style][ch];
+ if (metrics) {
+ return {
+ depth: metrics[0],
+ height: metrics[1],
+ italic: metrics[2],
+ skew: metrics[3],
+ width: metrics[4],
+ };
+ }
+};
+
+module.exports = {
+ metrics: metrics,
+ getCharacterMetrics: getCharacterMetrics,
+};
+
+},{"./Style":9,"./fontMetricsData":18,"./unicodeRegexes":24}],18:[function(require,module,exports){
+module.exports = {
+ "AMS-Regular": {
+ "65": [0, 0.68889, 0, 0],
+ "66": [0, 0.68889, 0, 0],
+ "67": [0, 0.68889, 0, 0],
+ "68": [0, 0.68889, 0, 0],
+ "69": [0, 0.68889, 0, 0],
+ "70": [0, 0.68889, 0, 0],
+ "71": [0, 0.68889, 0, 0],
+ "72": [0, 0.68889, 0, 0],
+ "73": [0, 0.68889, 0, 0],
+ "74": [0.16667, 0.68889, 0, 0],
+ "75": [0, 0.68889, 0, 0],
+ "76": [0, 0.68889, 0, 0],
+ "77": [0, 0.68889, 0, 0],
+ "78": [0, 0.68889, 0, 0],
+ "79": [0.16667, 0.68889, 0, 0],
+ "80": [0, 0.68889, 0, 0],
+ "81": [0.16667, 0.68889, 0, 0],
+ "82": [0, 0.68889, 0, 0],
+ "83": [0, 0.68889, 0, 0],
+ "84": [0, 0.68889, 0, 0],
+ "85": [0, 0.68889, 0, 0],
+ "86": [0, 0.68889, 0, 0],
+ "87": [0, 0.68889, 0, 0],
+ "88": [0, 0.68889, 0, 0],
+ "89": [0, 0.68889, 0, 0],
+ "90": [0, 0.68889, 0, 0],
+ "107": [0, 0.68889, 0, 0],
+ "165": [0, 0.675, 0.025, 0],
+ "174": [0.15559, 0.69224, 0, 0],
+ "240": [0, 0.68889, 0, 0],
+ "295": [0, 0.68889, 0, 0],
+ "710": [0, 0.825, 0, 0],
+ "732": [0, 0.9, 0, 0],
+ "770": [0, 0.825, 0, 0],
+ "771": [0, 0.9, 0, 0],
+ "989": [0.08167, 0.58167, 0, 0],
+ "1008": [0, 0.43056, 0.04028, 0],
+ "8245": [0, 0.54986, 0, 0],
+ "8463": [0, 0.68889, 0, 0],
+ "8487": [0, 0.68889, 0, 0],
+ "8498": [0, 0.68889, 0, 0],
+ "8502": [0, 0.68889, 0, 0],
+ "8503": [0, 0.68889, 0, 0],
+ "8504": [0, 0.68889, 0, 0],
+ "8513": [0, 0.68889, 0, 0],
+ "8592": [-0.03598, 0.46402, 0, 0],
+ "8594": [-0.03598, 0.46402, 0, 0],
+ "8602": [-0.13313, 0.36687, 0, 0],
+ "8603": [-0.13313, 0.36687, 0, 0],
+ "8606": [0.01354, 0.52239, 0, 0],
+ "8608": [0.01354, 0.52239, 0, 0],
+ "8610": [0.01354, 0.52239, 0, 0],
+ "8611": [0.01354, 0.52239, 0, 0],
+ "8619": [0, 0.54986, 0, 0],
+ "8620": [0, 0.54986, 0, 0],
+ "8621": [-0.13313, 0.37788, 0, 0],
+ "8622": [-0.13313, 0.36687, 0, 0],
+ "8624": [0, 0.69224, 0, 0],
+ "8625": [0, 0.69224, 0, 0],
+ "8630": [0, 0.43056, 0, 0],
+ "8631": [0, 0.43056, 0, 0],
+ "8634": [0.08198, 0.58198, 0, 0],
+ "8635": [0.08198, 0.58198, 0, 0],
+ "8638": [0.19444, 0.69224, 0, 0],
+ "8639": [0.19444, 0.69224, 0, 0],
+ "8642": [0.19444, 0.69224, 0, 0],
+ "8643": [0.19444, 0.69224, 0, 0],
+ "8644": [0.1808, 0.675, 0, 0],
+ "8646": [0.1808, 0.675, 0, 0],
+ "8647": [0.1808, 0.675, 0, 0],
+ "8648": [0.19444, 0.69224, 0, 0],
+ "8649": [0.1808, 0.675, 0, 0],
+ "8650": [0.19444, 0.69224, 0, 0],
+ "8651": [0.01354, 0.52239, 0, 0],
+ "8652": [0.01354, 0.52239, 0, 0],
+ "8653": [-0.13313, 0.36687, 0, 0],
+ "8654": [-0.13313, 0.36687, 0, 0],
+ "8655": [-0.13313, 0.36687, 0, 0],
+ "8666": [0.13667, 0.63667, 0, 0],
+ "8667": [0.13667, 0.63667, 0, 0],
+ "8669": [-0.13313, 0.37788, 0, 0],
+ "8672": [-0.064, 0.437, 0, 0],
+ "8674": [-0.064, 0.437, 0, 0],
+ "8705": [0, 0.825, 0, 0],
+ "8708": [0, 0.68889, 0, 0],
+ "8709": [0.08167, 0.58167, 0, 0],
+ "8717": [0, 0.43056, 0, 0],
+ "8722": [-0.03598, 0.46402, 0, 0],
+ "8724": [0.08198, 0.69224, 0, 0],
+ "8726": [0.08167, 0.58167, 0, 0],
+ "8733": [0, 0.69224, 0, 0],
+ "8736": [0, 0.69224, 0, 0],
+ "8737": [0, 0.69224, 0, 0],
+ "8738": [0.03517, 0.52239, 0, 0],
+ "8739": [0.08167, 0.58167, 0, 0],
+ "8740": [0.25142, 0.74111, 0, 0],
+ "8741": [0.08167, 0.58167, 0, 0],
+ "8742": [0.25142, 0.74111, 0, 0],
+ "8756": [0, 0.69224, 0, 0],
+ "8757": [0, 0.69224, 0, 0],
+ "8764": [-0.13313, 0.36687, 0, 0],
+ "8765": [-0.13313, 0.37788, 0, 0],
+ "8769": [-0.13313, 0.36687, 0, 0],
+ "8770": [-0.03625, 0.46375, 0, 0],
+ "8774": [0.30274, 0.79383, 0, 0],
+ "8776": [-0.01688, 0.48312, 0, 0],
+ "8778": [0.08167, 0.58167, 0, 0],
+ "8782": [0.06062, 0.54986, 0, 0],
+ "8783": [0.06062, 0.54986, 0, 0],
+ "8785": [0.08198, 0.58198, 0, 0],
+ "8786": [0.08198, 0.58198, 0, 0],
+ "8787": [0.08198, 0.58198, 0, 0],
+ "8790": [0, 0.69224, 0, 0],
+ "8791": [0.22958, 0.72958, 0, 0],
+ "8796": [0.08198, 0.91667, 0, 0],
+ "8806": [0.25583, 0.75583, 0, 0],
+ "8807": [0.25583, 0.75583, 0, 0],
+ "8808": [0.25142, 0.75726, 0, 0],
+ "8809": [0.25142, 0.75726, 0, 0],
+ "8812": [0.25583, 0.75583, 0, 0],
+ "8814": [0.20576, 0.70576, 0, 0],
+ "8815": [0.20576, 0.70576, 0, 0],
+ "8816": [0.30274, 0.79383, 0, 0],
+ "8817": [0.30274, 0.79383, 0, 0],
+ "8818": [0.22958, 0.72958, 0, 0],
+ "8819": [0.22958, 0.72958, 0, 0],
+ "8822": [0.1808, 0.675, 0, 0],
+ "8823": [0.1808, 0.675, 0, 0],
+ "8828": [0.13667, 0.63667, 0, 0],
+ "8829": [0.13667, 0.63667, 0, 0],
+ "8830": [0.22958, 0.72958, 0, 0],
+ "8831": [0.22958, 0.72958, 0, 0],
+ "8832": [0.20576, 0.70576, 0, 0],
+ "8833": [0.20576, 0.70576, 0, 0],
+ "8840": [0.30274, 0.79383, 0, 0],
+ "8841": [0.30274, 0.79383, 0, 0],
+ "8842": [0.13597, 0.63597, 0, 0],
+ "8843": [0.13597, 0.63597, 0, 0],
+ "8847": [0.03517, 0.54986, 0, 0],
+ "8848": [0.03517, 0.54986, 0, 0],
+ "8858": [0.08198, 0.58198, 0, 0],
+ "8859": [0.08198, 0.58198, 0, 0],
+ "8861": [0.08198, 0.58198, 0, 0],
+ "8862": [0, 0.675, 0, 0],
+ "8863": [0, 0.675, 0, 0],
+ "8864": [0, 0.675, 0, 0],
+ "8865": [0, 0.675, 0, 0],
+ "8872": [0, 0.69224, 0, 0],
+ "8873": [0, 0.69224, 0, 0],
+ "8874": [0, 0.69224, 0, 0],
+ "8876": [0, 0.68889, 0, 0],
+ "8877": [0, 0.68889, 0, 0],
+ "8878": [0, 0.68889, 0, 0],
+ "8879": [0, 0.68889, 0, 0],
+ "8882": [0.03517, 0.54986, 0, 0],
+ "8883": [0.03517, 0.54986, 0, 0],
+ "8884": [0.13667, 0.63667, 0, 0],
+ "8885": [0.13667, 0.63667, 0, 0],
+ "8888": [0, 0.54986, 0, 0],
+ "8890": [0.19444, 0.43056, 0, 0],
+ "8891": [0.19444, 0.69224, 0, 0],
+ "8892": [0.19444, 0.69224, 0, 0],
+ "8901": [0, 0.54986, 0, 0],
+ "8903": [0.08167, 0.58167, 0, 0],
+ "8905": [0.08167, 0.58167, 0, 0],
+ "8906": [0.08167, 0.58167, 0, 0],
+ "8907": [0, 0.69224, 0, 0],
+ "8908": [0, 0.69224, 0, 0],
+ "8909": [-0.03598, 0.46402, 0, 0],
+ "8910": [0, 0.54986, 0, 0],
+ "8911": [0, 0.54986, 0, 0],
+ "8912": [0.03517, 0.54986, 0, 0],
+ "8913": [0.03517, 0.54986, 0, 0],
+ "8914": [0, 0.54986, 0, 0],
+ "8915": [0, 0.54986, 0, 0],
+ "8916": [0, 0.69224, 0, 0],
+ "8918": [0.0391, 0.5391, 0, 0],
+ "8919": [0.0391, 0.5391, 0, 0],
+ "8920": [0.03517, 0.54986, 0, 0],
+ "8921": [0.03517, 0.54986, 0, 0],
+ "8922": [0.38569, 0.88569, 0, 0],
+ "8923": [0.38569, 0.88569, 0, 0],
+ "8926": [0.13667, 0.63667, 0, 0],
+ "8927": [0.13667, 0.63667, 0, 0],
+ "8928": [0.30274, 0.79383, 0, 0],
+ "8929": [0.30274, 0.79383, 0, 0],
+ "8934": [0.23222, 0.74111, 0, 0],
+ "8935": [0.23222, 0.74111, 0, 0],
+ "8936": [0.23222, 0.74111, 0, 0],
+ "8937": [0.23222, 0.74111, 0, 0],
+ "8938": [0.20576, 0.70576, 0, 0],
+ "8939": [0.20576, 0.70576, 0, 0],
+ "8940": [0.30274, 0.79383, 0, 0],
+ "8941": [0.30274, 0.79383, 0, 0],
+ "8994": [0.19444, 0.69224, 0, 0],
+ "8995": [0.19444, 0.69224, 0, 0],
+ "9416": [0.15559, 0.69224, 0, 0],
+ "9484": [0, 0.69224, 0, 0],
+ "9488": [0, 0.69224, 0, 0],
+ "9492": [0, 0.37788, 0, 0],
+ "9496": [0, 0.37788, 0, 0],
+ "9585": [0.19444, 0.68889, 0, 0],
+ "9586": [0.19444, 0.74111, 0, 0],
+ "9632": [0, 0.675, 0, 0],
+ "9633": [0, 0.675, 0, 0],
+ "9650": [0, 0.54986, 0, 0],
+ "9651": [0, 0.54986, 0, 0],
+ "9654": [0.03517, 0.54986, 0, 0],
+ "9660": [0, 0.54986, 0, 0],
+ "9661": [0, 0.54986, 0, 0],
+ "9664": [0.03517, 0.54986, 0, 0],
+ "9674": [0.11111, 0.69224, 0, 0],
+ "9733": [0.19444, 0.69224, 0, 0],
+ "10003": [0, 0.69224, 0, 0],
+ "10016": [0, 0.69224, 0, 0],
+ "10731": [0.11111, 0.69224, 0, 0],
+ "10846": [0.19444, 0.75583, 0, 0],
+ "10877": [0.13667, 0.63667, 0, 0],
+ "10878": [0.13667, 0.63667, 0, 0],
+ "10885": [0.25583, 0.75583, 0, 0],
+ "10886": [0.25583, 0.75583, 0, 0],
+ "10887": [0.13597, 0.63597, 0, 0],
+ "10888": [0.13597, 0.63597, 0, 0],
+ "10889": [0.26167, 0.75726, 0, 0],
+ "10890": [0.26167, 0.75726, 0, 0],
+ "10891": [0.48256, 0.98256, 0, 0],
+ "10892": [0.48256, 0.98256, 0, 0],
+ "10901": [0.13667, 0.63667, 0, 0],
+ "10902": [0.13667, 0.63667, 0, 0],
+ "10933": [0.25142, 0.75726, 0, 0],
+ "10934": [0.25142, 0.75726, 0, 0],
+ "10935": [0.26167, 0.75726, 0, 0],
+ "10936": [0.26167, 0.75726, 0, 0],
+ "10937": [0.26167, 0.75726, 0, 0],
+ "10938": [0.26167, 0.75726, 0, 0],
+ "10949": [0.25583, 0.75583, 0, 0],
+ "10950": [0.25583, 0.75583, 0, 0],
+ "10955": [0.28481, 0.79383, 0, 0],
+ "10956": [0.28481, 0.79383, 0, 0],
+ "57350": [0.08167, 0.58167, 0, 0],
+ "57351": [0.08167, 0.58167, 0, 0],
+ "57352": [0.08167, 0.58167, 0, 0],
+ "57353": [0, 0.43056, 0.04028, 0],
+ "57356": [0.25142, 0.75726, 0, 0],
+ "57357": [0.25142, 0.75726, 0, 0],
+ "57358": [0.41951, 0.91951, 0, 0],
+ "57359": [0.30274, 0.79383, 0, 0],
+ "57360": [0.30274, 0.79383, 0, 0],
+ "57361": [0.41951, 0.91951, 0, 0],
+ "57366": [0.25142, 0.75726, 0, 0],
+ "57367": [0.25142, 0.75726, 0, 0],
+ "57368": [0.25142, 0.75726, 0, 0],
+ "57369": [0.25142, 0.75726, 0, 0],
+ "57370": [0.13597, 0.63597, 0, 0],
+ "57371": [0.13597, 0.63597, 0, 0],
+ },
+ "Caligraphic-Regular": {
+ "48": [0, 0.43056, 0, 0],
+ "49": [0, 0.43056, 0, 0],
+ "50": [0, 0.43056, 0, 0],
+ "51": [0.19444, 0.43056, 0, 0],
+ "52": [0.19444, 0.43056, 0, 0],
+ "53": [0.19444, 0.43056, 0, 0],
+ "54": [0, 0.64444, 0, 0],
+ "55": [0.19444, 0.43056, 0, 0],
+ "56": [0, 0.64444, 0, 0],
+ "57": [0.19444, 0.43056, 0, 0],
+ "65": [0, 0.68333, 0, 0.19445],
+ "66": [0, 0.68333, 0.03041, 0.13889],
+ "67": [0, 0.68333, 0.05834, 0.13889],
+ "68": [0, 0.68333, 0.02778, 0.08334],
+ "69": [0, 0.68333, 0.08944, 0.11111],
+ "70": [0, 0.68333, 0.09931, 0.11111],
+ "71": [0.09722, 0.68333, 0.0593, 0.11111],
+ "72": [0, 0.68333, 0.00965, 0.11111],
+ "73": [0, 0.68333, 0.07382, 0],
+ "74": [0.09722, 0.68333, 0.18472, 0.16667],
+ "75": [0, 0.68333, 0.01445, 0.05556],
+ "76": [0, 0.68333, 0, 0.13889],
+ "77": [0, 0.68333, 0, 0.13889],
+ "78": [0, 0.68333, 0.14736, 0.08334],
+ "79": [0, 0.68333, 0.02778, 0.11111],
+ "80": [0, 0.68333, 0.08222, 0.08334],
+ "81": [0.09722, 0.68333, 0, 0.11111],
+ "82": [0, 0.68333, 0, 0.08334],
+ "83": [0, 0.68333, 0.075, 0.13889],
+ "84": [0, 0.68333, 0.25417, 0],
+ "85": [0, 0.68333, 0.09931, 0.08334],
+ "86": [0, 0.68333, 0.08222, 0],
+ "87": [0, 0.68333, 0.08222, 0.08334],
+ "88": [0, 0.68333, 0.14643, 0.13889],
+ "89": [0.09722, 0.68333, 0.08222, 0.08334],
+ "90": [0, 0.68333, 0.07944, 0.13889],
+ },
+ "Fraktur-Regular": {
+ "33": [0, 0.69141, 0, 0],
+ "34": [0, 0.69141, 0, 0],
+ "38": [0, 0.69141, 0, 0],
+ "39": [0, 0.69141, 0, 0],
+ "40": [0.24982, 0.74947, 0, 0],
+ "41": [0.24982, 0.74947, 0, 0],
+ "42": [0, 0.62119, 0, 0],
+ "43": [0.08319, 0.58283, 0, 0],
+ "44": [0, 0.10803, 0, 0],
+ "45": [0.08319, 0.58283, 0, 0],
+ "46": [0, 0.10803, 0, 0],
+ "47": [0.24982, 0.74947, 0, 0],
+ "48": [0, 0.47534, 0, 0],
+ "49": [0, 0.47534, 0, 0],
+ "50": [0, 0.47534, 0, 0],
+ "51": [0.18906, 0.47534, 0, 0],
+ "52": [0.18906, 0.47534, 0, 0],
+ "53": [0.18906, 0.47534, 0, 0],
+ "54": [0, 0.69141, 0, 0],
+ "55": [0.18906, 0.47534, 0, 0],
+ "56": [0, 0.69141, 0, 0],
+ "57": [0.18906, 0.47534, 0, 0],
+ "58": [0, 0.47534, 0, 0],
+ "59": [0.12604, 0.47534, 0, 0],
+ "61": [-0.13099, 0.36866, 0, 0],
+ "63": [0, 0.69141, 0, 0],
+ "65": [0, 0.69141, 0, 0],
+ "66": [0, 0.69141, 0, 0],
+ "67": [0, 0.69141, 0, 0],
+ "68": [0, 0.69141, 0, 0],
+ "69": [0, 0.69141, 0, 0],
+ "70": [0.12604, 0.69141, 0, 0],
+ "71": [0, 0.69141, 0, 0],
+ "72": [0.06302, 0.69141, 0, 0],
+ "73": [0, 0.69141, 0, 0],
+ "74": [0.12604, 0.69141, 0, 0],
+ "75": [0, 0.69141, 0, 0],
+ "76": [0, 0.69141, 0, 0],
+ "77": [0, 0.69141, 0, 0],
+ "78": [0, 0.69141, 0, 0],
+ "79": [0, 0.69141, 0, 0],
+ "80": [0.18906, 0.69141, 0, 0],
+ "81": [0.03781, 0.69141, 0, 0],
+ "82": [0, 0.69141, 0, 0],
+ "83": [0, 0.69141, 0, 0],
+ "84": [0, 0.69141, 0, 0],
+ "85": [0, 0.69141, 0, 0],
+ "86": [0, 0.69141, 0, 0],
+ "87": [0, 0.69141, 0, 0],
+ "88": [0, 0.69141, 0, 0],
+ "89": [0.18906, 0.69141, 0, 0],
+ "90": [0.12604, 0.69141, 0, 0],
+ "91": [0.24982, 0.74947, 0, 0],
+ "93": [0.24982, 0.74947, 0, 0],
+ "94": [0, 0.69141, 0, 0],
+ "97": [0, 0.47534, 0, 0],
+ "98": [0, 0.69141, 0, 0],
+ "99": [0, 0.47534, 0, 0],
+ "100": [0, 0.62119, 0, 0],
+ "101": [0, 0.47534, 0, 0],
+ "102": [0.18906, 0.69141, 0, 0],
+ "103": [0.18906, 0.47534, 0, 0],
+ "104": [0.18906, 0.69141, 0, 0],
+ "105": [0, 0.69141, 0, 0],
+ "106": [0, 0.69141, 0, 0],
+ "107": [0, 0.69141, 0, 0],
+ "108": [0, 0.69141, 0, 0],
+ "109": [0, 0.47534, 0, 0],
+ "110": [0, 0.47534, 0, 0],
+ "111": [0, 0.47534, 0, 0],
+ "112": [0.18906, 0.52396, 0, 0],
+ "113": [0.18906, 0.47534, 0, 0],
+ "114": [0, 0.47534, 0, 0],
+ "115": [0, 0.47534, 0, 0],
+ "116": [0, 0.62119, 0, 0],
+ "117": [0, 0.47534, 0, 0],
+ "118": [0, 0.52396, 0, 0],
+ "119": [0, 0.52396, 0, 0],
+ "120": [0.18906, 0.47534, 0, 0],
+ "121": [0.18906, 0.47534, 0, 0],
+ "122": [0.18906, 0.47534, 0, 0],
+ "8216": [0, 0.69141, 0, 0],
+ "8217": [0, 0.69141, 0, 0],
+ "58112": [0, 0.62119, 0, 0],
+ "58113": [0, 0.62119, 0, 0],
+ "58114": [0.18906, 0.69141, 0, 0],
+ "58115": [0.18906, 0.69141, 0, 0],
+ "58116": [0.18906, 0.47534, 0, 0],
+ "58117": [0, 0.69141, 0, 0],
+ "58118": [0, 0.62119, 0, 0],
+ "58119": [0, 0.47534, 0, 0],
+ },
+ "Main-Bold": {
+ "33": [0, 0.69444, 0, 0],
+ "34": [0, 0.69444, 0, 0],
+ "35": [0.19444, 0.69444, 0, 0],
+ "36": [0.05556, 0.75, 0, 0],
+ "37": [0.05556, 0.75, 0, 0],
+ "38": [0, 0.69444, 0, 0],
+ "39": [0, 0.69444, 0, 0],
+ "40": [0.25, 0.75, 0, 0],
+ "41": [0.25, 0.75, 0, 0],
+ "42": [0, 0.75, 0, 0],
+ "43": [0.13333, 0.63333, 0, 0],
+ "44": [0.19444, 0.15556, 0, 0],
+ "45": [0, 0.44444, 0, 0],
+ "46": [0, 0.15556, 0, 0],
+ "47": [0.25, 0.75, 0, 0],
+ "48": [0, 0.64444, 0, 0],
+ "49": [0, 0.64444, 0, 0],
+ "50": [0, 0.64444, 0, 0],
+ "51": [0, 0.64444, 0, 0],
+ "52": [0, 0.64444, 0, 0],
+ "53": [0, 0.64444, 0, 0],
+ "54": [0, 0.64444, 0, 0],
+ "55": [0, 0.64444, 0, 0],
+ "56": [0, 0.64444, 0, 0],
+ "57": [0, 0.64444, 0, 0],
+ "58": [0, 0.44444, 0, 0],
+ "59": [0.19444, 0.44444, 0, 0],
+ "60": [0.08556, 0.58556, 0, 0],
+ "61": [-0.10889, 0.39111, 0, 0],
+ "62": [0.08556, 0.58556, 0, 0],
+ "63": [0, 0.69444, 0, 0],
+ "64": [0, 0.69444, 0, 0],
+ "65": [0, 0.68611, 0, 0],
+ "66": [0, 0.68611, 0, 0],
+ "67": [0, 0.68611, 0, 0],
+ "68": [0, 0.68611, 0, 0],
+ "69": [0, 0.68611, 0, 0],
+ "70": [0, 0.68611, 0, 0],
+ "71": [0, 0.68611, 0, 0],
+ "72": [0, 0.68611, 0, 0],
+ "73": [0, 0.68611, 0, 0],
+ "74": [0, 0.68611, 0, 0],
+ "75": [0, 0.68611, 0, 0],
+ "76": [0, 0.68611, 0, 0],
+ "77": [0, 0.68611, 0, 0],
+ "78": [0, 0.68611, 0, 0],
+ "79": [0, 0.68611, 0, 0],
+ "80": [0, 0.68611, 0, 0],
+ "81": [0.19444, 0.68611, 0, 0],
+ "82": [0, 0.68611, 0, 0],
+ "83": [0, 0.68611, 0, 0],
+ "84": [0, 0.68611, 0, 0],
+ "85": [0, 0.68611, 0, 0],
+ "86": [0, 0.68611, 0.01597, 0],
+ "87": [0, 0.68611, 0.01597, 0],
+ "88": [0, 0.68611, 0, 0],
+ "89": [0, 0.68611, 0.02875, 0],
+ "90": [0, 0.68611, 0, 0],
+ "91": [0.25, 0.75, 0, 0],
+ "92": [0.25, 0.75, 0, 0],
+ "93": [0.25, 0.75, 0, 0],
+ "94": [0, 0.69444, 0, 0],
+ "95": [0.31, 0.13444, 0.03194, 0],
+ "96": [0, 0.69444, 0, 0],
+ "97": [0, 0.44444, 0, 0],
+ "98": [0, 0.69444, 0, 0],
+ "99": [0, 0.44444, 0, 0],
+ "100": [0, 0.69444, 0, 0],
+ "101": [0, 0.44444, 0, 0],
+ "102": [0, 0.69444, 0.10903, 0],
+ "103": [0.19444, 0.44444, 0.01597, 0],
+ "104": [0, 0.69444, 0, 0],
+ "105": [0, 0.69444, 0, 0],
+ "106": [0.19444, 0.69444, 0, 0],
+ "107": [0, 0.69444, 0, 0],
+ "108": [0, 0.69444, 0, 0],
+ "109": [0, 0.44444, 0, 0],
+ "110": [0, 0.44444, 0, 0],
+ "111": [0, 0.44444, 0, 0],
+ "112": [0.19444, 0.44444, 0, 0],
+ "113": [0.19444, 0.44444, 0, 0],
+ "114": [0, 0.44444, 0, 0],
+ "115": [0, 0.44444, 0, 0],
+ "116": [0, 0.63492, 0, 0],
+ "117": [0, 0.44444, 0, 0],
+ "118": [0, 0.44444, 0.01597, 0],
+ "119": [0, 0.44444, 0.01597, 0],
+ "120": [0, 0.44444, 0, 0],
+ "121": [0.19444, 0.44444, 0.01597, 0],
+ "122": [0, 0.44444, 0, 0],
+ "123": [0.25, 0.75, 0, 0],
+ "124": [0.25, 0.75, 0, 0],
+ "125": [0.25, 0.75, 0, 0],
+ "126": [0.35, 0.34444, 0, 0],
+ "168": [0, 0.69444, 0, 0],
+ "172": [0, 0.44444, 0, 0],
+ "175": [0, 0.59611, 0, 0],
+ "176": [0, 0.69444, 0, 0],
+ "177": [0.13333, 0.63333, 0, 0],
+ "180": [0, 0.69444, 0, 0],
+ "215": [0.13333, 0.63333, 0, 0],
+ "247": [0.13333, 0.63333, 0, 0],
+ "305": [0, 0.44444, 0, 0],
+ "567": [0.19444, 0.44444, 0, 0],
+ "710": [0, 0.69444, 0, 0],
+ "711": [0, 0.63194, 0, 0],
+ "713": [0, 0.59611, 0, 0],
+ "714": [0, 0.69444, 0, 0],
+ "715": [0, 0.69444, 0, 0],
+ "728": [0, 0.69444, 0, 0],
+ "729": [0, 0.69444, 0, 0],
+ "730": [0, 0.69444, 0, 0],
+ "732": [0, 0.69444, 0, 0],
+ "768": [0, 0.69444, 0, 0],
+ "769": [0, 0.69444, 0, 0],
+ "770": [0, 0.69444, 0, 0],
+ "771": [0, 0.69444, 0, 0],
+ "772": [0, 0.59611, 0, 0],
+ "774": [0, 0.69444, 0, 0],
+ "775": [0, 0.69444, 0, 0],
+ "776": [0, 0.69444, 0, 0],
+ "778": [0, 0.69444, 0, 0],
+ "779": [0, 0.69444, 0, 0],
+ "780": [0, 0.63194, 0, 0],
+ "824": [0.19444, 0.69444, 0, 0],
+ "915": [0, 0.68611, 0, 0],
+ "916": [0, 0.68611, 0, 0],
+ "920": [0, 0.68611, 0, 0],
+ "923": [0, 0.68611, 0, 0],
+ "926": [0, 0.68611, 0, 0],
+ "928": [0, 0.68611, 0, 0],
+ "931": [0, 0.68611, 0, 0],
+ "933": [0, 0.68611, 0, 0],
+ "934": [0, 0.68611, 0, 0],
+ "936": [0, 0.68611, 0, 0],
+ "937": [0, 0.68611, 0, 0],
+ "8211": [0, 0.44444, 0.03194, 0],
+ "8212": [0, 0.44444, 0.03194, 0],
+ "8216": [0, 0.69444, 0, 0],
+ "8217": [0, 0.69444, 0, 0],
+ "8220": [0, 0.69444, 0, 0],
+ "8221": [0, 0.69444, 0, 0],
+ "8224": [0.19444, 0.69444, 0, 0],
+ "8225": [0.19444, 0.69444, 0, 0],
+ "8242": [0, 0.55556, 0, 0],
+ "8407": [0, 0.72444, 0.15486, 0],
+ "8463": [0, 0.69444, 0, 0],
+ "8465": [0, 0.69444, 0, 0],
+ "8467": [0, 0.69444, 0, 0],
+ "8472": [0.19444, 0.44444, 0, 0],
+ "8476": [0, 0.69444, 0, 0],
+ "8501": [0, 0.69444, 0, 0],
+ "8592": [-0.10889, 0.39111, 0, 0],
+ "8593": [0.19444, 0.69444, 0, 0],
+ "8594": [-0.10889, 0.39111, 0, 0],
+ "8595": [0.19444, 0.69444, 0, 0],
+ "8596": [-0.10889, 0.39111, 0, 0],
+ "8597": [0.25, 0.75, 0, 0],
+ "8598": [0.19444, 0.69444, 0, 0],
+ "8599": [0.19444, 0.69444, 0, 0],
+ "8600": [0.19444, 0.69444, 0, 0],
+ "8601": [0.19444, 0.69444, 0, 0],
+ "8636": [-0.10889, 0.39111, 0, 0],
+ "8637": [-0.10889, 0.39111, 0, 0],
+ "8640": [-0.10889, 0.39111, 0, 0],
+ "8641": [-0.10889, 0.39111, 0, 0],
+ "8656": [-0.10889, 0.39111, 0, 0],
+ "8657": [0.19444, 0.69444, 0, 0],
+ "8658": [-0.10889, 0.39111, 0, 0],
+ "8659": [0.19444, 0.69444, 0, 0],
+ "8660": [-0.10889, 0.39111, 0, 0],
+ "8661": [0.25, 0.75, 0, 0],
+ "8704": [0, 0.69444, 0, 0],
+ "8706": [0, 0.69444, 0.06389, 0],
+ "8707": [0, 0.69444, 0, 0],
+ "8709": [0.05556, 0.75, 0, 0],
+ "8711": [0, 0.68611, 0, 0],
+ "8712": [0.08556, 0.58556, 0, 0],
+ "8715": [0.08556, 0.58556, 0, 0],
+ "8722": [0.13333, 0.63333, 0, 0],
+ "8723": [0.13333, 0.63333, 0, 0],
+ "8725": [0.25, 0.75, 0, 0],
+ "8726": [0.25, 0.75, 0, 0],
+ "8727": [-0.02778, 0.47222, 0, 0],
+ "8728": [-0.02639, 0.47361, 0, 0],
+ "8729": [-0.02639, 0.47361, 0, 0],
+ "8730": [0.18, 0.82, 0, 0],
+ "8733": [0, 0.44444, 0, 0],
+ "8734": [0, 0.44444, 0, 0],
+ "8736": [0, 0.69224, 0, 0],
+ "8739": [0.25, 0.75, 0, 0],
+ "8741": [0.25, 0.75, 0, 0],
+ "8743": [0, 0.55556, 0, 0],
+ "8744": [0, 0.55556, 0, 0],
+ "8745": [0, 0.55556, 0, 0],
+ "8746": [0, 0.55556, 0, 0],
+ "8747": [0.19444, 0.69444, 0.12778, 0],
+ "8764": [-0.10889, 0.39111, 0, 0],
+ "8768": [0.19444, 0.69444, 0, 0],
+ "8771": [0.00222, 0.50222, 0, 0],
+ "8776": [0.02444, 0.52444, 0, 0],
+ "8781": [0.00222, 0.50222, 0, 0],
+ "8801": [0.00222, 0.50222, 0, 0],
+ "8804": [0.19667, 0.69667, 0, 0],
+ "8805": [0.19667, 0.69667, 0, 0],
+ "8810": [0.08556, 0.58556, 0, 0],
+ "8811": [0.08556, 0.58556, 0, 0],
+ "8826": [0.08556, 0.58556, 0, 0],
+ "8827": [0.08556, 0.58556, 0, 0],
+ "8834": [0.08556, 0.58556, 0, 0],
+ "8835": [0.08556, 0.58556, 0, 0],
+ "8838": [0.19667, 0.69667, 0, 0],
+ "8839": [0.19667, 0.69667, 0, 0],
+ "8846": [0, 0.55556, 0, 0],
+ "8849": [0.19667, 0.69667, 0, 0],
+ "8850": [0.19667, 0.69667, 0, 0],
+ "8851": [0, 0.55556, 0, 0],
+ "8852": [0, 0.55556, 0, 0],
+ "8853": [0.13333, 0.63333, 0, 0],
+ "8854": [0.13333, 0.63333, 0, 0],
+ "8855": [0.13333, 0.63333, 0, 0],
+ "8856": [0.13333, 0.63333, 0, 0],
+ "8857": [0.13333, 0.63333, 0, 0],
+ "8866": [0, 0.69444, 0, 0],
+ "8867": [0, 0.69444, 0, 0],
+ "8868": [0, 0.69444, 0, 0],
+ "8869": [0, 0.69444, 0, 0],
+ "8900": [-0.02639, 0.47361, 0, 0],
+ "8901": [-0.02639, 0.47361, 0, 0],
+ "8902": [-0.02778, 0.47222, 0, 0],
+ "8968": [0.25, 0.75, 0, 0],
+ "8969": [0.25, 0.75, 0, 0],
+ "8970": [0.25, 0.75, 0, 0],
+ "8971": [0.25, 0.75, 0, 0],
+ "8994": [-0.13889, 0.36111, 0, 0],
+ "8995": [-0.13889, 0.36111, 0, 0],
+ "9651": [0.19444, 0.69444, 0, 0],
+ "9657": [-0.02778, 0.47222, 0, 0],
+ "9661": [0.19444, 0.69444, 0, 0],
+ "9667": [-0.02778, 0.47222, 0, 0],
+ "9711": [0.19444, 0.69444, 0, 0],
+ "9824": [0.12963, 0.69444, 0, 0],
+ "9825": [0.12963, 0.69444, 0, 0],
+ "9826": [0.12963, 0.69444, 0, 0],
+ "9827": [0.12963, 0.69444, 0, 0],
+ "9837": [0, 0.75, 0, 0],
+ "9838": [0.19444, 0.69444, 0, 0],
+ "9839": [0.19444, 0.69444, 0, 0],
+ "10216": [0.25, 0.75, 0, 0],
+ "10217": [0.25, 0.75, 0, 0],
+ "10815": [0, 0.68611, 0, 0],
+ "10927": [0.19667, 0.69667, 0, 0],
+ "10928": [0.19667, 0.69667, 0, 0],
+ },
+ "Main-Italic": {
+ "33": [0, 0.69444, 0.12417, 0],
+ "34": [0, 0.69444, 0.06961, 0],
+ "35": [0.19444, 0.69444, 0.06616, 0],
+ "37": [0.05556, 0.75, 0.13639, 0],
+ "38": [0, 0.69444, 0.09694, 0],
+ "39": [0, 0.69444, 0.12417, 0],
+ "40": [0.25, 0.75, 0.16194, 0],
+ "41": [0.25, 0.75, 0.03694, 0],
+ "42": [0, 0.75, 0.14917, 0],
+ "43": [0.05667, 0.56167, 0.03694, 0],
+ "44": [0.19444, 0.10556, 0, 0],
+ "45": [0, 0.43056, 0.02826, 0],
+ "46": [0, 0.10556, 0, 0],
+ "47": [0.25, 0.75, 0.16194, 0],
+ "48": [0, 0.64444, 0.13556, 0],
+ "49": [0, 0.64444, 0.13556, 0],
+ "50": [0, 0.64444, 0.13556, 0],
+ "51": [0, 0.64444, 0.13556, 0],
+ "52": [0.19444, 0.64444, 0.13556, 0],
+ "53": [0, 0.64444, 0.13556, 0],
+ "54": [0, 0.64444, 0.13556, 0],
+ "55": [0.19444, 0.64444, 0.13556, 0],
+ "56": [0, 0.64444, 0.13556, 0],
+ "57": [0, 0.64444, 0.13556, 0],
+ "58": [0, 0.43056, 0.0582, 0],
+ "59": [0.19444, 0.43056, 0.0582, 0],
+ "61": [-0.13313, 0.36687, 0.06616, 0],
+ "63": [0, 0.69444, 0.1225, 0],
+ "64": [0, 0.69444, 0.09597, 0],
+ "65": [0, 0.68333, 0, 0],
+ "66": [0, 0.68333, 0.10257, 0],
+ "67": [0, 0.68333, 0.14528, 0],
+ "68": [0, 0.68333, 0.09403, 0],
+ "69": [0, 0.68333, 0.12028, 0],
+ "70": [0, 0.68333, 0.13305, 0],
+ "71": [0, 0.68333, 0.08722, 0],
+ "72": [0, 0.68333, 0.16389, 0],
+ "73": [0, 0.68333, 0.15806, 0],
+ "74": [0, 0.68333, 0.14028, 0],
+ "75": [0, 0.68333, 0.14528, 0],
+ "76": [0, 0.68333, 0, 0],
+ "77": [0, 0.68333, 0.16389, 0],
+ "78": [0, 0.68333, 0.16389, 0],
+ "79": [0, 0.68333, 0.09403, 0],
+ "80": [0, 0.68333, 0.10257, 0],
+ "81": [0.19444, 0.68333, 0.09403, 0],
+ "82": [0, 0.68333, 0.03868, 0],
+ "83": [0, 0.68333, 0.11972, 0],
+ "84": [0, 0.68333, 0.13305, 0],
+ "85": [0, 0.68333, 0.16389, 0],
+ "86": [0, 0.68333, 0.18361, 0],
+ "87": [0, 0.68333, 0.18361, 0],
+ "88": [0, 0.68333, 0.15806, 0],
+ "89": [0, 0.68333, 0.19383, 0],
+ "90": [0, 0.68333, 0.14528, 0],
+ "91": [0.25, 0.75, 0.1875, 0],
+ "93": [0.25, 0.75, 0.10528, 0],
+ "94": [0, 0.69444, 0.06646, 0],
+ "95": [0.31, 0.12056, 0.09208, 0],
+ "97": [0, 0.43056, 0.07671, 0],
+ "98": [0, 0.69444, 0.06312, 0],
+ "99": [0, 0.43056, 0.05653, 0],
+ "100": [0, 0.69444, 0.10333, 0],
+ "101": [0, 0.43056, 0.07514, 0],
+ "102": [0.19444, 0.69444, 0.21194, 0],
+ "103": [0.19444, 0.43056, 0.08847, 0],
+ "104": [0, 0.69444, 0.07671, 0],
+ "105": [0, 0.65536, 0.1019, 0],
+ "106": [0.19444, 0.65536, 0.14467, 0],
+ "107": [0, 0.69444, 0.10764, 0],
+ "108": [0, 0.69444, 0.10333, 0],
+ "109": [0, 0.43056, 0.07671, 0],
+ "110": [0, 0.43056, 0.07671, 0],
+ "111": [0, 0.43056, 0.06312, 0],
+ "112": [0.19444, 0.43056, 0.06312, 0],
+ "113": [0.19444, 0.43056, 0.08847, 0],
+ "114": [0, 0.43056, 0.10764, 0],
+ "115": [0, 0.43056, 0.08208, 0],
+ "116": [0, 0.61508, 0.09486, 0],
+ "117": [0, 0.43056, 0.07671, 0],
+ "118": [0, 0.43056, 0.10764, 0],
+ "119": [0, 0.43056, 0.10764, 0],
+ "120": [0, 0.43056, 0.12042, 0],
+ "121": [0.19444, 0.43056, 0.08847, 0],
+ "122": [0, 0.43056, 0.12292, 0],
+ "126": [0.35, 0.31786, 0.11585, 0],
+ "163": [0, 0.69444, 0, 0],
+ "305": [0, 0.43056, 0, 0.02778],
+ "567": [0.19444, 0.43056, 0, 0.08334],
+ "768": [0, 0.69444, 0, 0],
+ "769": [0, 0.69444, 0.09694, 0],
+ "770": [0, 0.69444, 0.06646, 0],
+ "771": [0, 0.66786, 0.11585, 0],
+ "772": [0, 0.56167, 0.10333, 0],
+ "774": [0, 0.69444, 0.10806, 0],
+ "775": [0, 0.66786, 0.11752, 0],
+ "776": [0, 0.66786, 0.10474, 0],
+ "778": [0, 0.69444, 0, 0],
+ "779": [0, 0.69444, 0.1225, 0],
+ "780": [0, 0.62847, 0.08295, 0],
+ "915": [0, 0.68333, 0.13305, 0],
+ "916": [0, 0.68333, 0, 0],
+ "920": [0, 0.68333, 0.09403, 0],
+ "923": [0, 0.68333, 0, 0],
+ "926": [0, 0.68333, 0.15294, 0],
+ "928": [0, 0.68333, 0.16389, 0],
+ "931": [0, 0.68333, 0.12028, 0],
+ "933": [0, 0.68333, 0.11111, 0],
+ "934": [0, 0.68333, 0.05986, 0],
+ "936": [0, 0.68333, 0.11111, 0],
+ "937": [0, 0.68333, 0.10257, 0],
+ "8211": [0, 0.43056, 0.09208, 0],
+ "8212": [0, 0.43056, 0.09208, 0],
+ "8216": [0, 0.69444, 0.12417, 0],
+ "8217": [0, 0.69444, 0.12417, 0],
+ "8220": [0, 0.69444, 0.1685, 0],
+ "8221": [0, 0.69444, 0.06961, 0],
+ "8463": [0, 0.68889, 0, 0],
+ },
+ "Main-Regular": {
+ "32": [0, 0, 0, 0],
+ "33": [0, 0.69444, 0, 0],
+ "34": [0, 0.69444, 0, 0],
+ "35": [0.19444, 0.69444, 0, 0],
+ "36": [0.05556, 0.75, 0, 0],
+ "37": [0.05556, 0.75, 0, 0],
+ "38": [0, 0.69444, 0, 0],
+ "39": [0, 0.69444, 0, 0],
+ "40": [0.25, 0.75, 0, 0],
+ "41": [0.25, 0.75, 0, 0],
+ "42": [0, 0.75, 0, 0],
+ "43": [0.08333, 0.58333, 0, 0],
+ "44": [0.19444, 0.10556, 0, 0],
+ "45": [0, 0.43056, 0, 0],
+ "46": [0, 0.10556, 0, 0],
+ "47": [0.25, 0.75, 0, 0],
+ "48": [0, 0.64444, 0, 0],
+ "49": [0, 0.64444, 0, 0],
+ "50": [0, 0.64444, 0, 0],
+ "51": [0, 0.64444, 0, 0],
+ "52": [0, 0.64444, 0, 0],
+ "53": [0, 0.64444, 0, 0],
+ "54": [0, 0.64444, 0, 0],
+ "55": [0, 0.64444, 0, 0],
+ "56": [0, 0.64444, 0, 0],
+ "57": [0, 0.64444, 0, 0],
+ "58": [0, 0.43056, 0, 0],
+ "59": [0.19444, 0.43056, 0, 0],
+ "60": [0.0391, 0.5391, 0, 0],
+ "61": [-0.13313, 0.36687, 0, 0],
+ "62": [0.0391, 0.5391, 0, 0],
+ "63": [0, 0.69444, 0, 0],
+ "64": [0, 0.69444, 0, 0],
+ "65": [0, 0.68333, 0, 0],
+ "66": [0, 0.68333, 0, 0],
+ "67": [0, 0.68333, 0, 0],
+ "68": [0, 0.68333, 0, 0],
+ "69": [0, 0.68333, 0, 0],
+ "70": [0, 0.68333, 0, 0],
+ "71": [0, 0.68333, 0, 0],
+ "72": [0, 0.68333, 0, 0],
+ "73": [0, 0.68333, 0, 0],
+ "74": [0, 0.68333, 0, 0],
+ "75": [0, 0.68333, 0, 0],
+ "76": [0, 0.68333, 0, 0],
+ "77": [0, 0.68333, 0, 0],
+ "78": [0, 0.68333, 0, 0],
+ "79": [0, 0.68333, 0, 0],
+ "80": [0, 0.68333, 0, 0],
+ "81": [0.19444, 0.68333, 0, 0],
+ "82": [0, 0.68333, 0, 0],
+ "83": [0, 0.68333, 0, 0],
+ "84": [0, 0.68333, 0, 0],
+ "85": [0, 0.68333, 0, 0],
+ "86": [0, 0.68333, 0.01389, 0],
+ "87": [0, 0.68333, 0.01389, 0],
+ "88": [0, 0.68333, 0, 0],
+ "89": [0, 0.68333, 0.025, 0],
+ "90": [0, 0.68333, 0, 0],
+ "91": [0.25, 0.75, 0, 0],
+ "92": [0.25, 0.75, 0, 0],
+ "93": [0.25, 0.75, 0, 0],
+ "94": [0, 0.69444, 0, 0],
+ "95": [0.31, 0.12056, 0.02778, 0],
+ "96": [0, 0.69444, 0, 0],
+ "97": [0, 0.43056, 0, 0],
+ "98": [0, 0.69444, 0, 0],
+ "99": [0, 0.43056, 0, 0],
+ "100": [0, 0.69444, 0, 0],
+ "101": [0, 0.43056, 0, 0],
+ "102": [0, 0.69444, 0.07778, 0],
+ "103": [0.19444, 0.43056, 0.01389, 0],
+ "104": [0, 0.69444, 0, 0],
+ "105": [0, 0.66786, 0, 0],
+ "106": [0.19444, 0.66786, 0, 0],
+ "107": [0, 0.69444, 0, 0],
+ "108": [0, 0.69444, 0, 0],
+ "109": [0, 0.43056, 0, 0],
+ "110": [0, 0.43056, 0, 0],
+ "111": [0, 0.43056, 0, 0],
+ "112": [0.19444, 0.43056, 0, 0],
+ "113": [0.19444, 0.43056, 0, 0],
+ "114": [0, 0.43056, 0, 0],
+ "115": [0, 0.43056, 0, 0],
+ "116": [0, 0.61508, 0, 0],
+ "117": [0, 0.43056, 0, 0],
+ "118": [0, 0.43056, 0.01389, 0],
+ "119": [0, 0.43056, 0.01389, 0],
+ "120": [0, 0.43056, 0, 0],
+ "121": [0.19444, 0.43056, 0.01389, 0],
+ "122": [0, 0.43056, 0, 0],
+ "123": [0.25, 0.75, 0, 0],
+ "124": [0.25, 0.75, 0, 0],
+ "125": [0.25, 0.75, 0, 0],
+ "126": [0.35, 0.31786, 0, 0],
+ "160": [0, 0, 0, 0],
+ "168": [0, 0.66786, 0, 0],
+ "172": [0, 0.43056, 0, 0],
+ "175": [0, 0.56778, 0, 0],
+ "176": [0, 0.69444, 0, 0],
+ "177": [0.08333, 0.58333, 0, 0],
+ "180": [0, 0.69444, 0, 0],
+ "215": [0.08333, 0.58333, 0, 0],
+ "247": [0.08333, 0.58333, 0, 0],
+ "305": [0, 0.43056, 0, 0],
+ "567": [0.19444, 0.43056, 0, 0],
+ "710": [0, 0.69444, 0, 0],
+ "711": [0, 0.62847, 0, 0],
+ "713": [0, 0.56778, 0, 0],
+ "714": [0, 0.69444, 0, 0],
+ "715": [0, 0.69444, 0, 0],
+ "728": [0, 0.69444, 0, 0],
+ "729": [0, 0.66786, 0, 0],
+ "730": [0, 0.69444, 0, 0],
+ "732": [0, 0.66786, 0, 0],
+ "768": [0, 0.69444, 0, 0],
+ "769": [0, 0.69444, 0, 0],
+ "770": [0, 0.69444, 0, 0],
+ "771": [0, 0.66786, 0, 0],
+ "772": [0, 0.56778, 0, 0],
+ "774": [0, 0.69444, 0, 0],
+ "775": [0, 0.66786, 0, 0],
+ "776": [0, 0.66786, 0, 0],
+ "778": [0, 0.69444, 0, 0],
+ "779": [0, 0.69444, 0, 0],
+ "780": [0, 0.62847, 0, 0],
+ "824": [0.19444, 0.69444, 0, 0],
+ "915": [0, 0.68333, 0, 0],
+ "916": [0, 0.68333, 0, 0],
+ "920": [0, 0.68333, 0, 0],
+ "923": [0, 0.68333, 0, 0],
+ "926": [0, 0.68333, 0, 0],
+ "928": [0, 0.68333, 0, 0],
+ "931": [0, 0.68333, 0, 0],
+ "933": [0, 0.68333, 0, 0],
+ "934": [0, 0.68333, 0, 0],
+ "936": [0, 0.68333, 0, 0],
+ "937": [0, 0.68333, 0, 0],
+ "8211": [0, 0.43056, 0.02778, 0],
+ "8212": [0, 0.43056, 0.02778, 0],
+ "8216": [0, 0.69444, 0, 0],
+ "8217": [0, 0.69444, 0, 0],
+ "8220": [0, 0.69444, 0, 0],
+ "8221": [0, 0.69444, 0, 0],
+ "8224": [0.19444, 0.69444, 0, 0],
+ "8225": [0.19444, 0.69444, 0, 0],
+ "8230": [0, 0.12, 0, 0],
+ "8242": [0, 0.55556, 0, 0],
+ "8407": [0, 0.71444, 0.15382, 0],
+ "8463": [0, 0.68889, 0, 0],
+ "8465": [0, 0.69444, 0, 0],
+ "8467": [0, 0.69444, 0, 0.11111],
+ "8472": [0.19444, 0.43056, 0, 0.11111],
+ "8476": [0, 0.69444, 0, 0],
+ "8501": [0, 0.69444, 0, 0],
+ "8592": [-0.13313, 0.36687, 0, 0],
+ "8593": [0.19444, 0.69444, 0, 0],
+ "8594": [-0.13313, 0.36687, 0, 0],
+ "8595": [0.19444, 0.69444, 0, 0],
+ "8596": [-0.13313, 0.36687, 0, 0],
+ "8597": [0.25, 0.75, 0, 0],
+ "8598": [0.19444, 0.69444, 0, 0],
+ "8599": [0.19444, 0.69444, 0, 0],
+ "8600": [0.19444, 0.69444, 0, 0],
+ "8601": [0.19444, 0.69444, 0, 0],
+ "8614": [0.011, 0.511, 0, 0],
+ "8617": [0.011, 0.511, 0, 0],
+ "8618": [0.011, 0.511, 0, 0],
+ "8636": [-0.13313, 0.36687, 0, 0],
+ "8637": [-0.13313, 0.36687, 0, 0],
+ "8640": [-0.13313, 0.36687, 0, 0],
+ "8641": [-0.13313, 0.36687, 0, 0],
+ "8652": [0.011, 0.671, 0, 0],
+ "8656": [-0.13313, 0.36687, 0, 0],
+ "8657": [0.19444, 0.69444, 0, 0],
+ "8658": [-0.13313, 0.36687, 0, 0],
+ "8659": [0.19444, 0.69444, 0, 0],
+ "8660": [-0.13313, 0.36687, 0, 0],
+ "8661": [0.25, 0.75, 0, 0],
+ "8704": [0, 0.69444, 0, 0],
+ "8706": [0, 0.69444, 0.05556, 0.08334],
+ "8707": [0, 0.69444, 0, 0],
+ "8709": [0.05556, 0.75, 0, 0],
+ "8711": [0, 0.68333, 0, 0],
+ "8712": [0.0391, 0.5391, 0, 0],
+ "8715": [0.0391, 0.5391, 0, 0],
+ "8722": [0.08333, 0.58333, 0, 0],
+ "8723": [0.08333, 0.58333, 0, 0],
+ "8725": [0.25, 0.75, 0, 0],
+ "8726": [0.25, 0.75, 0, 0],
+ "8727": [-0.03472, 0.46528, 0, 0],
+ "8728": [-0.05555, 0.44445, 0, 0],
+ "8729": [-0.05555, 0.44445, 0, 0],
+ "8730": [0.2, 0.8, 0, 0],
+ "8733": [0, 0.43056, 0, 0],
+ "8734": [0, 0.43056, 0, 0],
+ "8736": [0, 0.69224, 0, 0],
+ "8739": [0.25, 0.75, 0, 0],
+ "8741": [0.25, 0.75, 0, 0],
+ "8743": [0, 0.55556, 0, 0],
+ "8744": [0, 0.55556, 0, 0],
+ "8745": [0, 0.55556, 0, 0],
+ "8746": [0, 0.55556, 0, 0],
+ "8747": [0.19444, 0.69444, 0.11111, 0],
+ "8764": [-0.13313, 0.36687, 0, 0],
+ "8768": [0.19444, 0.69444, 0, 0],
+ "8771": [-0.03625, 0.46375, 0, 0],
+ "8773": [-0.022, 0.589, 0, 0],
+ "8776": [-0.01688, 0.48312, 0, 0],
+ "8781": [-0.03625, 0.46375, 0, 0],
+ "8784": [-0.133, 0.67, 0, 0],
+ "8800": [0.215, 0.716, 0, 0],
+ "8801": [-0.03625, 0.46375, 0, 0],
+ "8804": [0.13597, 0.63597, 0, 0],
+ "8805": [0.13597, 0.63597, 0, 0],
+ "8810": [0.0391, 0.5391, 0, 0],
+ "8811": [0.0391, 0.5391, 0, 0],
+ "8826": [0.0391, 0.5391, 0, 0],
+ "8827": [0.0391, 0.5391, 0, 0],
+ "8834": [0.0391, 0.5391, 0, 0],
+ "8835": [0.0391, 0.5391, 0, 0],
+ "8838": [0.13597, 0.63597, 0, 0],
+ "8839": [0.13597, 0.63597, 0, 0],
+ "8846": [0, 0.55556, 0, 0],
+ "8849": [0.13597, 0.63597, 0, 0],
+ "8850": [0.13597, 0.63597, 0, 0],
+ "8851": [0, 0.55556, 0, 0],
+ "8852": [0, 0.55556, 0, 0],
+ "8853": [0.08333, 0.58333, 0, 0],
+ "8854": [0.08333, 0.58333, 0, 0],
+ "8855": [0.08333, 0.58333, 0, 0],
+ "8856": [0.08333, 0.58333, 0, 0],
+ "8857": [0.08333, 0.58333, 0, 0],
+ "8866": [0, 0.69444, 0, 0],
+ "8867": [0, 0.69444, 0, 0],
+ "8868": [0, 0.69444, 0, 0],
+ "8869": [0, 0.69444, 0, 0],
+ "8872": [0.249, 0.75, 0, 0],
+ "8900": [-0.05555, 0.44445, 0, 0],
+ "8901": [-0.05555, 0.44445, 0, 0],
+ "8902": [-0.03472, 0.46528, 0, 0],
+ "8904": [0.005, 0.505, 0, 0],
+ "8942": [0.03, 0.9, 0, 0],
+ "8943": [-0.19, 0.31, 0, 0],
+ "8945": [-0.1, 0.82, 0, 0],
+ "8968": [0.25, 0.75, 0, 0],
+ "8969": [0.25, 0.75, 0, 0],
+ "8970": [0.25, 0.75, 0, 0],
+ "8971": [0.25, 0.75, 0, 0],
+ "8994": [-0.14236, 0.35764, 0, 0],
+ "8995": [-0.14236, 0.35764, 0, 0],
+ "9136": [0.244, 0.744, 0, 0],
+ "9137": [0.244, 0.744, 0, 0],
+ "9651": [0.19444, 0.69444, 0, 0],
+ "9657": [-0.03472, 0.46528, 0, 0],
+ "9661": [0.19444, 0.69444, 0, 0],
+ "9667": [-0.03472, 0.46528, 0, 0],
+ "9711": [0.19444, 0.69444, 0, 0],
+ "9824": [0.12963, 0.69444, 0, 0],
+ "9825": [0.12963, 0.69444, 0, 0],
+ "9826": [0.12963, 0.69444, 0, 0],
+ "9827": [0.12963, 0.69444, 0, 0],
+ "9837": [0, 0.75, 0, 0],
+ "9838": [0.19444, 0.69444, 0, 0],
+ "9839": [0.19444, 0.69444, 0, 0],
+ "10216": [0.25, 0.75, 0, 0],
+ "10217": [0.25, 0.75, 0, 0],
+ "10222": [0.244, 0.744, 0, 0],
+ "10223": [0.244, 0.744, 0, 0],
+ "10229": [0.011, 0.511, 0, 0],
+ "10230": [0.011, 0.511, 0, 0],
+ "10231": [0.011, 0.511, 0, 0],
+ "10232": [0.024, 0.525, 0, 0],
+ "10233": [0.024, 0.525, 0, 0],
+ "10234": [0.024, 0.525, 0, 0],
+ "10236": [0.011, 0.511, 0, 0],
+ "10815": [0, 0.68333, 0, 0],
+ "10927": [0.13597, 0.63597, 0, 0],
+ "10928": [0.13597, 0.63597, 0, 0],
+ },
+ "Math-BoldItalic": {
+ "47": [0.19444, 0.69444, 0, 0],
+ "65": [0, 0.68611, 0, 0],
+ "66": [0, 0.68611, 0.04835, 0],
+ "67": [0, 0.68611, 0.06979, 0],
+ "68": [0, 0.68611, 0.03194, 0],
+ "69": [0, 0.68611, 0.05451, 0],
+ "70": [0, 0.68611, 0.15972, 0],
+ "71": [0, 0.68611, 0, 0],
+ "72": [0, 0.68611, 0.08229, 0],
+ "73": [0, 0.68611, 0.07778, 0],
+ "74": [0, 0.68611, 0.10069, 0],
+ "75": [0, 0.68611, 0.06979, 0],
+ "76": [0, 0.68611, 0, 0],
+ "77": [0, 0.68611, 0.11424, 0],
+ "78": [0, 0.68611, 0.11424, 0],
+ "79": [0, 0.68611, 0.03194, 0],
+ "80": [0, 0.68611, 0.15972, 0],
+ "81": [0.19444, 0.68611, 0, 0],
+ "82": [0, 0.68611, 0.00421, 0],
+ "83": [0, 0.68611, 0.05382, 0],
+ "84": [0, 0.68611, 0.15972, 0],
+ "85": [0, 0.68611, 0.11424, 0],
+ "86": [0, 0.68611, 0.25555, 0],
+ "87": [0, 0.68611, 0.15972, 0],
+ "88": [0, 0.68611, 0.07778, 0],
+ "89": [0, 0.68611, 0.25555, 0],
+ "90": [0, 0.68611, 0.06979, 0],
+ "97": [0, 0.44444, 0, 0],
+ "98": [0, 0.69444, 0, 0],
+ "99": [0, 0.44444, 0, 0],
+ "100": [0, 0.69444, 0, 0],
+ "101": [0, 0.44444, 0, 0],
+ "102": [0.19444, 0.69444, 0.11042, 0],
+ "103": [0.19444, 0.44444, 0.03704, 0],
+ "104": [0, 0.69444, 0, 0],
+ "105": [0, 0.69326, 0, 0],
+ "106": [0.19444, 0.69326, 0.0622, 0],
+ "107": [0, 0.69444, 0.01852, 0],
+ "108": [0, 0.69444, 0.0088, 0],
+ "109": [0, 0.44444, 0, 0],
+ "110": [0, 0.44444, 0, 0],
+ "111": [0, 0.44444, 0, 0],
+ "112": [0.19444, 0.44444, 0, 0],
+ "113": [0.19444, 0.44444, 0.03704, 0],
+ "114": [0, 0.44444, 0.03194, 0],
+ "115": [0, 0.44444, 0, 0],
+ "116": [0, 0.63492, 0, 0],
+ "117": [0, 0.44444, 0, 0],
+ "118": [0, 0.44444, 0.03704, 0],
+ "119": [0, 0.44444, 0.02778, 0],
+ "120": [0, 0.44444, 0, 0],
+ "121": [0.19444, 0.44444, 0.03704, 0],
+ "122": [0, 0.44444, 0.04213, 0],
+ "915": [0, 0.68611, 0.15972, 0],
+ "916": [0, 0.68611, 0, 0],
+ "920": [0, 0.68611, 0.03194, 0],
+ "923": [0, 0.68611, 0, 0],
+ "926": [0, 0.68611, 0.07458, 0],
+ "928": [0, 0.68611, 0.08229, 0],
+ "931": [0, 0.68611, 0.05451, 0],
+ "933": [0, 0.68611, 0.15972, 0],
+ "934": [0, 0.68611, 0, 0],
+ "936": [0, 0.68611, 0.11653, 0],
+ "937": [0, 0.68611, 0.04835, 0],
+ "945": [0, 0.44444, 0, 0],
+ "946": [0.19444, 0.69444, 0.03403, 0],
+ "947": [0.19444, 0.44444, 0.06389, 0],
+ "948": [0, 0.69444, 0.03819, 0],
+ "949": [0, 0.44444, 0, 0],
+ "950": [0.19444, 0.69444, 0.06215, 0],
+ "951": [0.19444, 0.44444, 0.03704, 0],
+ "952": [0, 0.69444, 0.03194, 0],
+ "953": [0, 0.44444, 0, 0],
+ "954": [0, 0.44444, 0, 0],
+ "955": [0, 0.69444, 0, 0],
+ "956": [0.19444, 0.44444, 0, 0],
+ "957": [0, 0.44444, 0.06898, 0],
+ "958": [0.19444, 0.69444, 0.03021, 0],
+ "959": [0, 0.44444, 0, 0],
+ "960": [0, 0.44444, 0.03704, 0],
+ "961": [0.19444, 0.44444, 0, 0],
+ "962": [0.09722, 0.44444, 0.07917, 0],
+ "963": [0, 0.44444, 0.03704, 0],
+ "964": [0, 0.44444, 0.13472, 0],
+ "965": [0, 0.44444, 0.03704, 0],
+ "966": [0.19444, 0.44444, 0, 0],
+ "967": [0.19444, 0.44444, 0, 0],
+ "968": [0.19444, 0.69444, 0.03704, 0],
+ "969": [0, 0.44444, 0.03704, 0],
+ "977": [0, 0.69444, 0, 0],
+ "981": [0.19444, 0.69444, 0, 0],
+ "982": [0, 0.44444, 0.03194, 0],
+ "1009": [0.19444, 0.44444, 0, 0],
+ "1013": [0, 0.44444, 0, 0],
+ },
+ "Math-Italic": {
+ "47": [0.19444, 0.69444, 0, 0],
+ "65": [0, 0.68333, 0, 0.13889],
+ "66": [0, 0.68333, 0.05017, 0.08334],
+ "67": [0, 0.68333, 0.07153, 0.08334],
+ "68": [0, 0.68333, 0.02778, 0.05556],
+ "69": [0, 0.68333, 0.05764, 0.08334],
+ "70": [0, 0.68333, 0.13889, 0.08334],
+ "71": [0, 0.68333, 0, 0.08334],
+ "72": [0, 0.68333, 0.08125, 0.05556],
+ "73": [0, 0.68333, 0.07847, 0.11111],
+ "74": [0, 0.68333, 0.09618, 0.16667],
+ "75": [0, 0.68333, 0.07153, 0.05556],
+ "76": [0, 0.68333, 0, 0.02778],
+ "77": [0, 0.68333, 0.10903, 0.08334],
+ "78": [0, 0.68333, 0.10903, 0.08334],
+ "79": [0, 0.68333, 0.02778, 0.08334],
+ "80": [0, 0.68333, 0.13889, 0.08334],
+ "81": [0.19444, 0.68333, 0, 0.08334],
+ "82": [0, 0.68333, 0.00773, 0.08334],
+ "83": [0, 0.68333, 0.05764, 0.08334],
+ "84": [0, 0.68333, 0.13889, 0.08334],
+ "85": [0, 0.68333, 0.10903, 0.02778],
+ "86": [0, 0.68333, 0.22222, 0],
+ "87": [0, 0.68333, 0.13889, 0],
+ "88": [0, 0.68333, 0.07847, 0.08334],
+ "89": [0, 0.68333, 0.22222, 0],
+ "90": [0, 0.68333, 0.07153, 0.08334],
+ "97": [0, 0.43056, 0, 0],
+ "98": [0, 0.69444, 0, 0],
+ "99": [0, 0.43056, 0, 0.05556],
+ "100": [0, 0.69444, 0, 0.16667],
+ "101": [0, 0.43056, 0, 0.05556],
+ "102": [0.19444, 0.69444, 0.10764, 0.16667],
+ "103": [0.19444, 0.43056, 0.03588, 0.02778],
+ "104": [0, 0.69444, 0, 0],
+ "105": [0, 0.65952, 0, 0],
+ "106": [0.19444, 0.65952, 0.05724, 0],
+ "107": [0, 0.69444, 0.03148, 0],
+ "108": [0, 0.69444, 0.01968, 0.08334],
+ "109": [0, 0.43056, 0, 0],
+ "110": [0, 0.43056, 0, 0],
+ "111": [0, 0.43056, 0, 0.05556],
+ "112": [0.19444, 0.43056, 0, 0.08334],
+ "113": [0.19444, 0.43056, 0.03588, 0.08334],
+ "114": [0, 0.43056, 0.02778, 0.05556],
+ "115": [0, 0.43056, 0, 0.05556],
+ "116": [0, 0.61508, 0, 0.08334],
+ "117": [0, 0.43056, 0, 0.02778],
+ "118": [0, 0.43056, 0.03588, 0.02778],
+ "119": [0, 0.43056, 0.02691, 0.08334],
+ "120": [0, 0.43056, 0, 0.02778],
+ "121": [0.19444, 0.43056, 0.03588, 0.05556],
+ "122": [0, 0.43056, 0.04398, 0.05556],
+ "915": [0, 0.68333, 0.13889, 0.08334],
+ "916": [0, 0.68333, 0, 0.16667],
+ "920": [0, 0.68333, 0.02778, 0.08334],
+ "923": [0, 0.68333, 0, 0.16667],
+ "926": [0, 0.68333, 0.07569, 0.08334],
+ "928": [0, 0.68333, 0.08125, 0.05556],
+ "931": [0, 0.68333, 0.05764, 0.08334],
+ "933": [0, 0.68333, 0.13889, 0.05556],
+ "934": [0, 0.68333, 0, 0.08334],
+ "936": [0, 0.68333, 0.11, 0.05556],
+ "937": [0, 0.68333, 0.05017, 0.08334],
+ "945": [0, 0.43056, 0.0037, 0.02778],
+ "946": [0.19444, 0.69444, 0.05278, 0.08334],
+ "947": [0.19444, 0.43056, 0.05556, 0],
+ "948": [0, 0.69444, 0.03785, 0.05556],
+ "949": [0, 0.43056, 0, 0.08334],
+ "950": [0.19444, 0.69444, 0.07378, 0.08334],
+ "951": [0.19444, 0.43056, 0.03588, 0.05556],
+ "952": [0, 0.69444, 0.02778, 0.08334],
+ "953": [0, 0.43056, 0, 0.05556],
+ "954": [0, 0.43056, 0, 0],
+ "955": [0, 0.69444, 0, 0],
+ "956": [0.19444, 0.43056, 0, 0.02778],
+ "957": [0, 0.43056, 0.06366, 0.02778],
+ "958": [0.19444, 0.69444, 0.04601, 0.11111],
+ "959": [0, 0.43056, 0, 0.05556],
+ "960": [0, 0.43056, 0.03588, 0],
+ "961": [0.19444, 0.43056, 0, 0.08334],
+ "962": [0.09722, 0.43056, 0.07986, 0.08334],
+ "963": [0, 0.43056, 0.03588, 0],
+ "964": [0, 0.43056, 0.1132, 0.02778],
+ "965": [0, 0.43056, 0.03588, 0.02778],
+ "966": [0.19444, 0.43056, 0, 0.08334],
+ "967": [0.19444, 0.43056, 0, 0.05556],
+ "968": [0.19444, 0.69444, 0.03588, 0.11111],
+ "969": [0, 0.43056, 0.03588, 0],
+ "977": [0, 0.69444, 0, 0.08334],
+ "981": [0.19444, 0.69444, 0, 0.08334],
+ "982": [0, 0.43056, 0.02778, 0],
+ "1009": [0.19444, 0.43056, 0, 0.08334],
+ "1013": [0, 0.43056, 0, 0.05556],
+ },
+ "Math-Regular": {
+ "65": [0, 0.68333, 0, 0.13889],
+ "66": [0, 0.68333, 0.05017, 0.08334],
+ "67": [0, 0.68333, 0.07153, 0.08334],
+ "68": [0, 0.68333, 0.02778, 0.05556],
+ "69": [0, 0.68333, 0.05764, 0.08334],
+ "70": [0, 0.68333, 0.13889, 0.08334],
+ "71": [0, 0.68333, 0, 0.08334],
+ "72": [0, 0.68333, 0.08125, 0.05556],
+ "73": [0, 0.68333, 0.07847, 0.11111],
+ "74": [0, 0.68333, 0.09618, 0.16667],
+ "75": [0, 0.68333, 0.07153, 0.05556],
+ "76": [0, 0.68333, 0, 0.02778],
+ "77": [0, 0.68333, 0.10903, 0.08334],
+ "78": [0, 0.68333, 0.10903, 0.08334],
+ "79": [0, 0.68333, 0.02778, 0.08334],
+ "80": [0, 0.68333, 0.13889, 0.08334],
+ "81": [0.19444, 0.68333, 0, 0.08334],
+ "82": [0, 0.68333, 0.00773, 0.08334],
+ "83": [0, 0.68333, 0.05764, 0.08334],
+ "84": [0, 0.68333, 0.13889, 0.08334],
+ "85": [0, 0.68333, 0.10903, 0.02778],
+ "86": [0, 0.68333, 0.22222, 0],
+ "87": [0, 0.68333, 0.13889, 0],
+ "88": [0, 0.68333, 0.07847, 0.08334],
+ "89": [0, 0.68333, 0.22222, 0],
+ "90": [0, 0.68333, 0.07153, 0.08334],
+ "97": [0, 0.43056, 0, 0],
+ "98": [0, 0.69444, 0, 0],
+ "99": [0, 0.43056, 0, 0.05556],
+ "100": [0, 0.69444, 0, 0.16667],
+ "101": [0, 0.43056, 0, 0.05556],
+ "102": [0.19444, 0.69444, 0.10764, 0.16667],
+ "103": [0.19444, 0.43056, 0.03588, 0.02778],
+ "104": [0, 0.69444, 0, 0],
+ "105": [0, 0.65952, 0, 0],
+ "106": [0.19444, 0.65952, 0.05724, 0],
+ "107": [0, 0.69444, 0.03148, 0],
+ "108": [0, 0.69444, 0.01968, 0.08334],
+ "109": [0, 0.43056, 0, 0],
+ "110": [0, 0.43056, 0, 0],
+ "111": [0, 0.43056, 0, 0.05556],
+ "112": [0.19444, 0.43056, 0, 0.08334],
+ "113": [0.19444, 0.43056, 0.03588, 0.08334],
+ "114": [0, 0.43056, 0.02778, 0.05556],
+ "115": [0, 0.43056, 0, 0.05556],
+ "116": [0, 0.61508, 0, 0.08334],
+ "117": [0, 0.43056, 0, 0.02778],
+ "118": [0, 0.43056, 0.03588, 0.02778],
+ "119": [0, 0.43056, 0.02691, 0.08334],
+ "120": [0, 0.43056, 0, 0.02778],
+ "121": [0.19444, 0.43056, 0.03588, 0.05556],
+ "122": [0, 0.43056, 0.04398, 0.05556],
+ "915": [0, 0.68333, 0.13889, 0.08334],
+ "916": [0, 0.68333, 0, 0.16667],
+ "920": [0, 0.68333, 0.02778, 0.08334],
+ "923": [0, 0.68333, 0, 0.16667],
+ "926": [0, 0.68333, 0.07569, 0.08334],
+ "928": [0, 0.68333, 0.08125, 0.05556],
+ "931": [0, 0.68333, 0.05764, 0.08334],
+ "933": [0, 0.68333, 0.13889, 0.05556],
+ "934": [0, 0.68333, 0, 0.08334],
+ "936": [0, 0.68333, 0.11, 0.05556],
+ "937": [0, 0.68333, 0.05017, 0.08334],
+ "945": [0, 0.43056, 0.0037, 0.02778],
+ "946": [0.19444, 0.69444, 0.05278, 0.08334],
+ "947": [0.19444, 0.43056, 0.05556, 0],
+ "948": [0, 0.69444, 0.03785, 0.05556],
+ "949": [0, 0.43056, 0, 0.08334],
+ "950": [0.19444, 0.69444, 0.07378, 0.08334],
+ "951": [0.19444, 0.43056, 0.03588, 0.05556],
+ "952": [0, 0.69444, 0.02778, 0.08334],
+ "953": [0, 0.43056, 0, 0.05556],
+ "954": [0, 0.43056, 0, 0],
+ "955": [0, 0.69444, 0, 0],
+ "956": [0.19444, 0.43056, 0, 0.02778],
+ "957": [0, 0.43056, 0.06366, 0.02778],
+ "958": [0.19444, 0.69444, 0.04601, 0.11111],
+ "959": [0, 0.43056, 0, 0.05556],
+ "960": [0, 0.43056, 0.03588, 0],
+ "961": [0.19444, 0.43056, 0, 0.08334],
+ "962": [0.09722, 0.43056, 0.07986, 0.08334],
+ "963": [0, 0.43056, 0.03588, 0],
+ "964": [0, 0.43056, 0.1132, 0.02778],
+ "965": [0, 0.43056, 0.03588, 0.02778],
+ "966": [0.19444, 0.43056, 0, 0.08334],
+ "967": [0.19444, 0.43056, 0, 0.05556],
+ "968": [0.19444, 0.69444, 0.03588, 0.11111],
+ "969": [0, 0.43056, 0.03588, 0],
+ "977": [0, 0.69444, 0, 0.08334],
+ "981": [0.19444, 0.69444, 0, 0.08334],
+ "982": [0, 0.43056, 0.02778, 0],
+ "1009": [0.19444, 0.43056, 0, 0.08334],
+ "1013": [0, 0.43056, 0, 0.05556],
+ },
+ "SansSerif-Regular": {
+ "33": [0, 0.69444, 0, 0],
+ "34": [0, 0.69444, 0, 0],
+ "35": [0.19444, 0.69444, 0, 0],
+ "36": [0.05556, 0.75, 0, 0],
+ "37": [0.05556, 0.75, 0, 0],
+ "38": [0, 0.69444, 0, 0],
+ "39": [0, 0.69444, 0, 0],
+ "40": [0.25, 0.75, 0, 0],
+ "41": [0.25, 0.75, 0, 0],
+ "42": [0, 0.75, 0, 0],
+ "43": [0.08333, 0.58333, 0, 0],
+ "44": [0.125, 0.08333, 0, 0],
+ "45": [0, 0.44444, 0, 0],
+ "46": [0, 0.08333, 0, 0],
+ "47": [0.25, 0.75, 0, 0],
+ "48": [0, 0.65556, 0, 0],
+ "49": [0, 0.65556, 0, 0],
+ "50": [0, 0.65556, 0, 0],
+ "51": [0, 0.65556, 0, 0],
+ "52": [0, 0.65556, 0, 0],
+ "53": [0, 0.65556, 0, 0],
+ "54": [0, 0.65556, 0, 0],
+ "55": [0, 0.65556, 0, 0],
+ "56": [0, 0.65556, 0, 0],
+ "57": [0, 0.65556, 0, 0],
+ "58": [0, 0.44444, 0, 0],
+ "59": [0.125, 0.44444, 0, 0],
+ "61": [-0.13, 0.37, 0, 0],
+ "63": [0, 0.69444, 0, 0],
+ "64": [0, 0.69444, 0, 0],
+ "65": [0, 0.69444, 0, 0],
+ "66": [0, 0.69444, 0, 0],
+ "67": [0, 0.69444, 0, 0],
+ "68": [0, 0.69444, 0, 0],
+ "69": [0, 0.69444, 0, 0],
+ "70": [0, 0.69444, 0, 0],
+ "71": [0, 0.69444, 0, 0],
+ "72": [0, 0.69444, 0, 0],
+ "73": [0, 0.69444, 0, 0],
+ "74": [0, 0.69444, 0, 0],
+ "75": [0, 0.69444, 0, 0],
+ "76": [0, 0.69444, 0, 0],
+ "77": [0, 0.69444, 0, 0],
+ "78": [0, 0.69444, 0, 0],
+ "79": [0, 0.69444, 0, 0],
+ "80": [0, 0.69444, 0, 0],
+ "81": [0.125, 0.69444, 0, 0],
+ "82": [0, 0.69444, 0, 0],
+ "83": [0, 0.69444, 0, 0],
+ "84": [0, 0.69444, 0, 0],
+ "85": [0, 0.69444, 0, 0],
+ "86": [0, 0.69444, 0.01389, 0],
+ "87": [0, 0.69444, 0.01389, 0],
+ "88": [0, 0.69444, 0, 0],
+ "89": [0, 0.69444, 0.025, 0],
+ "90": [0, 0.69444, 0, 0],
+ "91": [0.25, 0.75, 0, 0],
+ "93": [0.25, 0.75, 0, 0],
+ "94": [0, 0.69444, 0, 0],
+ "95": [0.35, 0.09444, 0.02778, 0],
+ "97": [0, 0.44444, 0, 0],
+ "98": [0, 0.69444, 0, 0],
+ "99": [0, 0.44444, 0, 0],
+ "100": [0, 0.69444, 0, 0],
+ "101": [0, 0.44444, 0, 0],
+ "102": [0, 0.69444, 0.06944, 0],
+ "103": [0.19444, 0.44444, 0.01389, 0],
+ "104": [0, 0.69444, 0, 0],
+ "105": [0, 0.67937, 0, 0],
+ "106": [0.19444, 0.67937, 0, 0],
+ "107": [0, 0.69444, 0, 0],
+ "108": [0, 0.69444, 0, 0],
+ "109": [0, 0.44444, 0, 0],
+ "110": [0, 0.44444, 0, 0],
+ "111": [0, 0.44444, 0, 0],
+ "112": [0.19444, 0.44444, 0, 0],
+ "113": [0.19444, 0.44444, 0, 0],
+ "114": [0, 0.44444, 0.01389, 0],
+ "115": [0, 0.44444, 0, 0],
+ "116": [0, 0.57143, 0, 0],
+ "117": [0, 0.44444, 0, 0],
+ "118": [0, 0.44444, 0.01389, 0],
+ "119": [0, 0.44444, 0.01389, 0],
+ "120": [0, 0.44444, 0, 0],
+ "121": [0.19444, 0.44444, 0.01389, 0],
+ "122": [0, 0.44444, 0, 0],
+ "126": [0.35, 0.32659, 0, 0],
+ "305": [0, 0.44444, 0, 0],
+ "567": [0.19444, 0.44444, 0, 0],
+ "768": [0, 0.69444, 0, 0],
+ "769": [0, 0.69444, 0, 0],
+ "770": [0, 0.69444, 0, 0],
+ "771": [0, 0.67659, 0, 0],
+ "772": [0, 0.60889, 0, 0],
+ "774": [0, 0.69444, 0, 0],
+ "775": [0, 0.67937, 0, 0],
+ "776": [0, 0.67937, 0, 0],
+ "778": [0, 0.69444, 0, 0],
+ "779": [0, 0.69444, 0, 0],
+ "780": [0, 0.63194, 0, 0],
+ "915": [0, 0.69444, 0, 0],
+ "916": [0, 0.69444, 0, 0],
+ "920": [0, 0.69444, 0, 0],
+ "923": [0, 0.69444, 0, 0],
+ "926": [0, 0.69444, 0, 0],
+ "928": [0, 0.69444, 0, 0],
+ "931": [0, 0.69444, 0, 0],
+ "933": [0, 0.69444, 0, 0],
+ "934": [0, 0.69444, 0, 0],
+ "936": [0, 0.69444, 0, 0],
+ "937": [0, 0.69444, 0, 0],
+ "8211": [0, 0.44444, 0.02778, 0],
+ "8212": [0, 0.44444, 0.02778, 0],
+ "8216": [0, 0.69444, 0, 0],
+ "8217": [0, 0.69444, 0, 0],
+ "8220": [0, 0.69444, 0, 0],
+ "8221": [0, 0.69444, 0, 0],
+ },
+ "Script-Regular": {
+ "65": [0, 0.7, 0.22925, 0],
+ "66": [0, 0.7, 0.04087, 0],
+ "67": [0, 0.7, 0.1689, 0],
+ "68": [0, 0.7, 0.09371, 0],
+ "69": [0, 0.7, 0.18583, 0],
+ "70": [0, 0.7, 0.13634, 0],
+ "71": [0, 0.7, 0.17322, 0],
+ "72": [0, 0.7, 0.29694, 0],
+ "73": [0, 0.7, 0.19189, 0],
+ "74": [0.27778, 0.7, 0.19189, 0],
+ "75": [0, 0.7, 0.31259, 0],
+ "76": [0, 0.7, 0.19189, 0],
+ "77": [0, 0.7, 0.15981, 0],
+ "78": [0, 0.7, 0.3525, 0],
+ "79": [0, 0.7, 0.08078, 0],
+ "80": [0, 0.7, 0.08078, 0],
+ "81": [0, 0.7, 0.03305, 0],
+ "82": [0, 0.7, 0.06259, 0],
+ "83": [0, 0.7, 0.19189, 0],
+ "84": [0, 0.7, 0.29087, 0],
+ "85": [0, 0.7, 0.25815, 0],
+ "86": [0, 0.7, 0.27523, 0],
+ "87": [0, 0.7, 0.27523, 0],
+ "88": [0, 0.7, 0.26006, 0],
+ "89": [0, 0.7, 0.2939, 0],
+ "90": [0, 0.7, 0.24037, 0],
+ },
+ "Size1-Regular": {
+ "40": [0.35001, 0.85, 0, 0],
+ "41": [0.35001, 0.85, 0, 0],
+ "47": [0.35001, 0.85, 0, 0],
+ "91": [0.35001, 0.85, 0, 0],
+ "92": [0.35001, 0.85, 0, 0],
+ "93": [0.35001, 0.85, 0, 0],
+ "123": [0.35001, 0.85, 0, 0],
+ "125": [0.35001, 0.85, 0, 0],
+ "710": [0, 0.72222, 0, 0],
+ "732": [0, 0.72222, 0, 0],
+ "770": [0, 0.72222, 0, 0],
+ "771": [0, 0.72222, 0, 0],
+ "8214": [-0.00099, 0.601, 0, 0],
+ "8593": [1e-05, 0.6, 0, 0],
+ "8595": [1e-05, 0.6, 0, 0],
+ "8657": [1e-05, 0.6, 0, 0],
+ "8659": [1e-05, 0.6, 0, 0],
+ "8719": [0.25001, 0.75, 0, 0],
+ "8720": [0.25001, 0.75, 0, 0],
+ "8721": [0.25001, 0.75, 0, 0],
+ "8730": [0.35001, 0.85, 0, 0],
+ "8739": [-0.00599, 0.606, 0, 0],
+ "8741": [-0.00599, 0.606, 0, 0],
+ "8747": [0.30612, 0.805, 0.19445, 0],
+ "8748": [0.306, 0.805, 0.19445, 0],
+ "8749": [0.306, 0.805, 0.19445, 0],
+ "8750": [0.30612, 0.805, 0.19445, 0],
+ "8896": [0.25001, 0.75, 0, 0],
+ "8897": [0.25001, 0.75, 0, 0],
+ "8898": [0.25001, 0.75, 0, 0],
+ "8899": [0.25001, 0.75, 0, 0],
+ "8968": [0.35001, 0.85, 0, 0],
+ "8969": [0.35001, 0.85, 0, 0],
+ "8970": [0.35001, 0.85, 0, 0],
+ "8971": [0.35001, 0.85, 0, 0],
+ "9168": [-0.00099, 0.601, 0, 0],
+ "10216": [0.35001, 0.85, 0, 0],
+ "10217": [0.35001, 0.85, 0, 0],
+ "10752": [0.25001, 0.75, 0, 0],
+ "10753": [0.25001, 0.75, 0, 0],
+ "10754": [0.25001, 0.75, 0, 0],
+ "10756": [0.25001, 0.75, 0, 0],
+ "10758": [0.25001, 0.75, 0, 0],
+ },
+ "Size2-Regular": {
+ "40": [0.65002, 1.15, 0, 0],
+ "41": [0.65002, 1.15, 0, 0],
+ "47": [0.65002, 1.15, 0, 0],
+ "91": [0.65002, 1.15, 0, 0],
+ "92": [0.65002, 1.15, 0, 0],
+ "93": [0.65002, 1.15, 0, 0],
+ "123": [0.65002, 1.15, 0, 0],
+ "125": [0.65002, 1.15, 0, 0],
+ "710": [0, 0.75, 0, 0],
+ "732": [0, 0.75, 0, 0],
+ "770": [0, 0.75, 0, 0],
+ "771": [0, 0.75, 0, 0],
+ "8719": [0.55001, 1.05, 0, 0],
+ "8720": [0.55001, 1.05, 0, 0],
+ "8721": [0.55001, 1.05, 0, 0],
+ "8730": [0.65002, 1.15, 0, 0],
+ "8747": [0.86225, 1.36, 0.44445, 0],
+ "8748": [0.862, 1.36, 0.44445, 0],
+ "8749": [0.862, 1.36, 0.44445, 0],
+ "8750": [0.86225, 1.36, 0.44445, 0],
+ "8896": [0.55001, 1.05, 0, 0],
+ "8897": [0.55001, 1.05, 0, 0],
+ "8898": [0.55001, 1.05, 0, 0],
+ "8899": [0.55001, 1.05, 0, 0],
+ "8968": [0.65002, 1.15, 0, 0],
+ "8969": [0.65002, 1.15, 0, 0],
+ "8970": [0.65002, 1.15, 0, 0],
+ "8971": [0.65002, 1.15, 0, 0],
+ "10216": [0.65002, 1.15, 0, 0],
+ "10217": [0.65002, 1.15, 0, 0],
+ "10752": [0.55001, 1.05, 0, 0],
+ "10753": [0.55001, 1.05, 0, 0],
+ "10754": [0.55001, 1.05, 0, 0],
+ "10756": [0.55001, 1.05, 0, 0],
+ "10758": [0.55001, 1.05, 0, 0],
+ },
+ "Size3-Regular": {
+ "40": [0.95003, 1.45, 0, 0],
+ "41": [0.95003, 1.45, 0, 0],
+ "47": [0.95003, 1.45, 0, 0],
+ "91": [0.95003, 1.45, 0, 0],
+ "92": [0.95003, 1.45, 0, 0],
+ "93": [0.95003, 1.45, 0, 0],
+ "123": [0.95003, 1.45, 0, 0],
+ "125": [0.95003, 1.45, 0, 0],
+ "710": [0, 0.75, 0, 0],
+ "732": [0, 0.75, 0, 0],
+ "770": [0, 0.75, 0, 0],
+ "771": [0, 0.75, 0, 0],
+ "8730": [0.95003, 1.45, 0, 0],
+ "8968": [0.95003, 1.45, 0, 0],
+ "8969": [0.95003, 1.45, 0, 0],
+ "8970": [0.95003, 1.45, 0, 0],
+ "8971": [0.95003, 1.45, 0, 0],
+ "10216": [0.95003, 1.45, 0, 0],
+ "10217": [0.95003, 1.45, 0, 0],
+ },
+ "Size4-Regular": {
+ "40": [1.25003, 1.75, 0, 0],
+ "41": [1.25003, 1.75, 0, 0],
+ "47": [1.25003, 1.75, 0, 0],
+ "91": [1.25003, 1.75, 0, 0],
+ "92": [1.25003, 1.75, 0, 0],
+ "93": [1.25003, 1.75, 0, 0],
+ "123": [1.25003, 1.75, 0, 0],
+ "125": [1.25003, 1.75, 0, 0],
+ "710": [0, 0.825, 0, 0],
+ "732": [0, 0.825, 0, 0],
+ "770": [0, 0.825, 0, 0],
+ "771": [0, 0.825, 0, 0],
+ "8730": [1.25003, 1.75, 0, 0],
+ "8968": [1.25003, 1.75, 0, 0],
+ "8969": [1.25003, 1.75, 0, 0],
+ "8970": [1.25003, 1.75, 0, 0],
+ "8971": [1.25003, 1.75, 0, 0],
+ "9115": [0.64502, 1.155, 0, 0],
+ "9116": [1e-05, 0.6, 0, 0],
+ "9117": [0.64502, 1.155, 0, 0],
+ "9118": [0.64502, 1.155, 0, 0],
+ "9119": [1e-05, 0.6, 0, 0],
+ "9120": [0.64502, 1.155, 0, 0],
+ "9121": [0.64502, 1.155, 0, 0],
+ "9122": [-0.00099, 0.601, 0, 0],
+ "9123": [0.64502, 1.155, 0, 0],
+ "9124": [0.64502, 1.155, 0, 0],
+ "9125": [-0.00099, 0.601, 0, 0],
+ "9126": [0.64502, 1.155, 0, 0],
+ "9127": [1e-05, 0.9, 0, 0],
+ "9128": [0.65002, 1.15, 0, 0],
+ "9129": [0.90001, 0, 0, 0],
+ "9130": [0, 0.3, 0, 0],
+ "9131": [1e-05, 0.9, 0, 0],
+ "9132": [0.65002, 1.15, 0, 0],
+ "9133": [0.90001, 0, 0, 0],
+ "9143": [0.88502, 0.915, 0, 0],
+ "10216": [1.25003, 1.75, 0, 0],
+ "10217": [1.25003, 1.75, 0, 0],
+ "57344": [-0.00499, 0.605, 0, 0],
+ "57345": [-0.00499, 0.605, 0, 0],
+ "57680": [0, 0.12, 0, 0],
+ "57681": [0, 0.12, 0, 0],
+ "57682": [0, 0.12, 0, 0],
+ "57683": [0, 0.12, 0, 0],
+ },
+ "Typewriter-Regular": {
+ "33": [0, 0.61111, 0, 0],
+ "34": [0, 0.61111, 0, 0],
+ "35": [0, 0.61111, 0, 0],
+ "36": [0.08333, 0.69444, 0, 0],
+ "37": [0.08333, 0.69444, 0, 0],
+ "38": [0, 0.61111, 0, 0],
+ "39": [0, 0.61111, 0, 0],
+ "40": [0.08333, 0.69444, 0, 0],
+ "41": [0.08333, 0.69444, 0, 0],
+ "42": [0, 0.52083, 0, 0],
+ "43": [-0.08056, 0.53055, 0, 0],
+ "44": [0.13889, 0.125, 0, 0],
+ "45": [-0.08056, 0.53055, 0, 0],
+ "46": [0, 0.125, 0, 0],
+ "47": [0.08333, 0.69444, 0, 0],
+ "48": [0, 0.61111, 0, 0],
+ "49": [0, 0.61111, 0, 0],
+ "50": [0, 0.61111, 0, 0],
+ "51": [0, 0.61111, 0, 0],
+ "52": [0, 0.61111, 0, 0],
+ "53": [0, 0.61111, 0, 0],
+ "54": [0, 0.61111, 0, 0],
+ "55": [0, 0.61111, 0, 0],
+ "56": [0, 0.61111, 0, 0],
+ "57": [0, 0.61111, 0, 0],
+ "58": [0, 0.43056, 0, 0],
+ "59": [0.13889, 0.43056, 0, 0],
+ "60": [-0.05556, 0.55556, 0, 0],
+ "61": [-0.19549, 0.41562, 0, 0],
+ "62": [-0.05556, 0.55556, 0, 0],
+ "63": [0, 0.61111, 0, 0],
+ "64": [0, 0.61111, 0, 0],
+ "65": [0, 0.61111, 0, 0],
+ "66": [0, 0.61111, 0, 0],
+ "67": [0, 0.61111, 0, 0],
+ "68": [0, 0.61111, 0, 0],
+ "69": [0, 0.61111, 0, 0],
+ "70": [0, 0.61111, 0, 0],
+ "71": [0, 0.61111, 0, 0],
+ "72": [0, 0.61111, 0, 0],
+ "73": [0, 0.61111, 0, 0],
+ "74": [0, 0.61111, 0, 0],
+ "75": [0, 0.61111, 0, 0],
+ "76": [0, 0.61111, 0, 0],
+ "77": [0, 0.61111, 0, 0],
+ "78": [0, 0.61111, 0, 0],
+ "79": [0, 0.61111, 0, 0],
+ "80": [0, 0.61111, 0, 0],
+ "81": [0.13889, 0.61111, 0, 0],
+ "82": [0, 0.61111, 0, 0],
+ "83": [0, 0.61111, 0, 0],
+ "84": [0, 0.61111, 0, 0],
+ "85": [0, 0.61111, 0, 0],
+ "86": [0, 0.61111, 0, 0],
+ "87": [0, 0.61111, 0, 0],
+ "88": [0, 0.61111, 0, 0],
+ "89": [0, 0.61111, 0, 0],
+ "90": [0, 0.61111, 0, 0],
+ "91": [0.08333, 0.69444, 0, 0],
+ "92": [0.08333, 0.69444, 0, 0],
+ "93": [0.08333, 0.69444, 0, 0],
+ "94": [0, 0.61111, 0, 0],
+ "95": [0.09514, 0, 0, 0],
+ "96": [0, 0.61111, 0, 0],
+ "97": [0, 0.43056, 0, 0],
+ "98": [0, 0.61111, 0, 0],
+ "99": [0, 0.43056, 0, 0],
+ "100": [0, 0.61111, 0, 0],
+ "101": [0, 0.43056, 0, 0],
+ "102": [0, 0.61111, 0, 0],
+ "103": [0.22222, 0.43056, 0, 0],
+ "104": [0, 0.61111, 0, 0],
+ "105": [0, 0.61111, 0, 0],
+ "106": [0.22222, 0.61111, 0, 0],
+ "107": [0, 0.61111, 0, 0],
+ "108": [0, 0.61111, 0, 0],
+ "109": [0, 0.43056, 0, 0],
+ "110": [0, 0.43056, 0, 0],
+ "111": [0, 0.43056, 0, 0],
+ "112": [0.22222, 0.43056, 0, 0],
+ "113": [0.22222, 0.43056, 0, 0],
+ "114": [0, 0.43056, 0, 0],
+ "115": [0, 0.43056, 0, 0],
+ "116": [0, 0.55358, 0, 0],
+ "117": [0, 0.43056, 0, 0],
+ "118": [0, 0.43056, 0, 0],
+ "119": [0, 0.43056, 0, 0],
+ "120": [0, 0.43056, 0, 0],
+ "121": [0.22222, 0.43056, 0, 0],
+ "122": [0, 0.43056, 0, 0],
+ "123": [0.08333, 0.69444, 0, 0],
+ "124": [0.08333, 0.69444, 0, 0],
+ "125": [0.08333, 0.69444, 0, 0],
+ "126": [0, 0.61111, 0, 0],
+ "127": [0, 0.61111, 0, 0],
+ "305": [0, 0.43056, 0, 0],
+ "567": [0.22222, 0.43056, 0, 0],
+ "768": [0, 0.61111, 0, 0],
+ "769": [0, 0.61111, 0, 0],
+ "770": [0, 0.61111, 0, 0],
+ "771": [0, 0.61111, 0, 0],
+ "772": [0, 0.56555, 0, 0],
+ "774": [0, 0.61111, 0, 0],
+ "776": [0, 0.61111, 0, 0],
+ "778": [0, 0.61111, 0, 0],
+ "780": [0, 0.56597, 0, 0],
+ "915": [0, 0.61111, 0, 0],
+ "916": [0, 0.61111, 0, 0],
+ "920": [0, 0.61111, 0, 0],
+ "923": [0, 0.61111, 0, 0],
+ "926": [0, 0.61111, 0, 0],
+ "928": [0, 0.61111, 0, 0],
+ "931": [0, 0.61111, 0, 0],
+ "933": [0, 0.61111, 0, 0],
+ "934": [0, 0.61111, 0, 0],
+ "936": [0, 0.61111, 0, 0],
+ "937": [0, 0.61111, 0, 0],
+ "2018": [0, 0.61111, 0, 0],
+ "2019": [0, 0.61111, 0, 0],
+ "8242": [0, 0.61111, 0, 0],
+ },
+};
+
+},{}],19:[function(require,module,exports){
+var utils = require("./utils");
+var ParseError = require("./ParseError");
+
+/* This file contains a list of functions that we parse, identified by
+ * the calls to defineFunction.
+ *
+ * The first argument to defineFunction is a single name or a list of names.
+ * All functions named in such a list will share a single implementation.
+ *
+ * Each declared function can have associated properties, which
+ * include the following:
+ *
+ * - numArgs: The number of arguments the function takes.
+ * If this is the only property, it can be passed as a number
+ * instead of an element of a properties object.
+ * - argTypes: (optional) An array corresponding to each argument of the
+ * function, giving the type of argument that should be parsed. Its
+ * length should be equal to `numArgs + numOptionalArgs`. Valid
+ * types:
+ * - "size": A size-like thing, such as "1em" or "5ex"
+ * - "color": An html color, like "#abc" or "blue"
+ * - "original": The same type as the environment that the
+ * function being parsed is in (e.g. used for the
+ * bodies of functions like \color where the first
+ * argument is special and the second argument is
+ * parsed normally)
+ * Other possible types (probably shouldn't be used)
+ * - "text": Text-like (e.g. \text)
+ * - "math": Normal math
+ * If undefined, this will be treated as an appropriate length
+ * array of "original" strings
+ * - greediness: (optional) The greediness of the function to use ungrouped
+ * arguments.
+ *
+ * E.g. if you have an expression
+ * \sqrt \frac 1 2
+ * since \frac has greediness=2 vs \sqrt's greediness=1, \frac
+ * will use the two arguments '1' and '2' as its two arguments,
+ * then that whole function will be used as the argument to
+ * \sqrt. On the other hand, the expressions
+ * \frac \frac 1 2 3
+ * and
+ * \frac \sqrt 1 2
+ * will fail because \frac and \frac have equal greediness
+ * and \sqrt has a lower greediness than \frac respectively. To
+ * make these parse, we would have to change them to:
+ * \frac {\frac 1 2} 3
+ * and
+ * \frac {\sqrt 1} 2
+ *
+ * The default value is `1`
+ * - allowedInText: (optional) Whether or not the function is allowed inside
+ * text mode (default false)
+ * - numOptionalArgs: (optional) The number of optional arguments the function
+ * should parse. If the optional arguments aren't found,
+ * `null` will be passed to the handler in their place.
+ * (default 0)
+ * - infix: (optional) Must be true if the function is an infix operator.
+ *
+ * The last argument is that implementation, the handler for the function(s).
+ * It is called to handle these functions and their arguments.
+ * It receives two arguments:
+ * - context contains information and references provided by the parser
+ * - args is an array of arguments obtained from TeX input
+ * The context contains the following properties:
+ * - funcName: the text (i.e. name) of the function, including \
+ * - parser: the parser object
+ * - lexer: the lexer object
+ * - positions: the positions in the overall string of the function
+ * and the arguments.
+ * The latter three should only be used to produce error messages.
+ *
+ * The function should return an object with the following keys:
+ * - type: The type of element that this is. This is then used in
+ * buildHTML/buildMathML to determine which function
+ * should be called to build this node into a DOM node
+ * Any other data can be added to the object, which will be passed
+ * in to the function in buildHTML/buildMathML as `group.value`.
+ */
+
+function defineFunction(names, props, handler) {
+ if (typeof names === "string") {
+ names = [names];
+ }
+ if (typeof props === "number") {
+ props = { numArgs: props };
+ }
+ // Set default values of functions
+ var data = {
+ numArgs: props.numArgs,
+ argTypes: props.argTypes,
+ greediness: (props.greediness === undefined) ? 1 : props.greediness,
+ allowedInText: !!props.allowedInText,
+ numOptionalArgs: props.numOptionalArgs || 0,
+ infix: !!props.infix,
+ handler: handler,
+ };
+ for (var i = 0; i < names.length; ++i) {
+ module.exports[names[i]] = data;
+ }
+}
+
+// A normal square root
+defineFunction("\\sqrt", {
+ numArgs: 1,
+ numOptionalArgs: 1,
+}, function(context, args) {
+ var index = args[0];
+ var body = args[1];
+ return {
+ type: "sqrt",
+ body: body,
+ index: index,
+ };
+});
+
+// Some non-mathy text
+defineFunction("\\text", {
+ numArgs: 1,
+ argTypes: ["text"],
+ greediness: 2,
+}, function(context, args) {
+ var body = args[0];
+ // Since the corresponding buildHTML/buildMathML function expects a
+ // list of elements, we normalize for different kinds of arguments
+ // TODO(emily): maybe this should be done somewhere else
+ var inner;
+ if (body.type === "ordgroup") {
+ inner = body.value;
+ } else {
+ inner = [body];
+ }
+
+ return {
+ type: "text",
+ body: inner,
+ };
+});
+
+// A two-argument custom color
+defineFunction("\\color", {
+ numArgs: 2,
+ allowedInText: true,
+ greediness: 3,
+ argTypes: ["color", "original"],
+}, function(context, args) {
+ var color = args[0];
+ var body = args[1];
+ // Normalize the different kinds of bodies (see \text above)
+ var inner;
+ if (body.type === "ordgroup") {
+ inner = body.value;
+ } else {
+ inner = [body];
+ }
+
+ return {
+ type: "color",
+ color: color.value,
+ value: inner,
+ };
+});
+
+// An overline
+defineFunction("\\overline", {
+ numArgs: 1,
+}, function(context, args) {
+ var body = args[0];
+ return {
+ type: "overline",
+ body: body,
+ };
+});
+
+// An underline
+defineFunction("\\underline", {
+ numArgs: 1,
+}, function(context, args) {
+ var body = args[0];
+ return {
+ type: "underline",
+ body: body,
+ };
+});
+
+// A box of the width and height
+defineFunction("\\rule", {
+ numArgs: 2,
+ numOptionalArgs: 1,
+ argTypes: ["size", "size", "size"],
+}, function(context, args) {
+ var shift = args[0];
+ var width = args[1];
+ var height = args[2];
+ return {
+ type: "rule",
+ shift: shift && shift.value,
+ width: width.value,
+ height: height.value,
+ };
+});
+
+defineFunction("\\kern", {
+ numArgs: 1,
+ argTypes: ["size"],
+}, function(context, args) {
+ return {
+ type: "kern",
+ dimension: args[0].value,
+ };
+});
+
+// A KaTeX logo
+defineFunction("\\KaTeX", {
+ numArgs: 0,
+}, function(context) {
+ return {
+ type: "katex",
+ };
+});
+
+defineFunction("\\phantom", {
+ numArgs: 1,
+}, function(context, args) {
+ var body = args[0];
+ var inner;
+ if (body.type === "ordgroup") {
+ inner = body.value;
+ } else {
+ inner = [body];
+ }
+
+ return {
+ type: "phantom",
+ value: inner,
+ };
+});
+
+// Extra data needed for the delimiter handler down below
+var delimiterSizes = {
+ "\\bigl" : {type: "open", size: 1},
+ "\\Bigl" : {type: "open", size: 2},
+ "\\biggl": {type: "open", size: 3},
+ "\\Biggl": {type: "open", size: 4},
+ "\\bigr" : {type: "close", size: 1},
+ "\\Bigr" : {type: "close", size: 2},
+ "\\biggr": {type: "close", size: 3},
+ "\\Biggr": {type: "close", size: 4},
+ "\\bigm" : {type: "rel", size: 1},
+ "\\Bigm" : {type: "rel", size: 2},
+ "\\biggm": {type: "rel", size: 3},
+ "\\Biggm": {type: "rel", size: 4},
+ "\\big" : {type: "textord", size: 1},
+ "\\Big" : {type: "textord", size: 2},
+ "\\bigg" : {type: "textord", size: 3},
+ "\\Bigg" : {type: "textord", size: 4},
+};
+
+var delimiters = [
+ "(", ")", "[", "\\lbrack", "]", "\\rbrack",
+ "\\{", "\\lbrace", "\\}", "\\rbrace",
+ "\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
+ "<", ">", "\\langle", "\\rangle", "\\lt", "\\gt",
+ "\\lvert", "\\rvert", "\\lVert", "\\rVert",
+ "\\lgroup", "\\rgroup", "\\lmoustache", "\\rmoustache",
+ "/", "\\backslash",
+ "|", "\\vert", "\\|", "\\Vert",
+ "\\uparrow", "\\Uparrow",
+ "\\downarrow", "\\Downarrow",
+ "\\updownarrow", "\\Updownarrow",
+ ".",
+];
+
+var fontAliases = {
+ "\\Bbb": "\\mathbb",
+ "\\bold": "\\mathbf",
+ "\\frak": "\\mathfrak",
+};
+
+// Single-argument color functions
+defineFunction([
+ "\\blue", "\\orange", "\\pink", "\\red",
+ "\\green", "\\gray", "\\purple",
+ "\\blueA", "\\blueB", "\\blueC", "\\blueD", "\\blueE",
+ "\\tealA", "\\tealB", "\\tealC", "\\tealD", "\\tealE",
+ "\\greenA", "\\greenB", "\\greenC", "\\greenD", "\\greenE",
+ "\\goldA", "\\goldB", "\\goldC", "\\goldD", "\\goldE",
+ "\\redA", "\\redB", "\\redC", "\\redD", "\\redE",
+ "\\maroonA", "\\maroonB", "\\maroonC", "\\maroonD", "\\maroonE",
+ "\\purpleA", "\\purpleB", "\\purpleC", "\\purpleD", "\\purpleE",
+ "\\mintA", "\\mintB", "\\mintC",
+ "\\grayA", "\\grayB", "\\grayC", "\\grayD", "\\grayE",
+ "\\grayF", "\\grayG", "\\grayH", "\\grayI",
+ "\\kaBlue", "\\kaGreen",
+], {
+ numArgs: 1,
+ allowedInText: true,
+ greediness: 3,
+}, function(context, args) {
+ var body = args[0];
+ var atoms;
+ if (body.type === "ordgroup") {
+ atoms = body.value;
+ } else {
+ atoms = [body];
+ }
+
+ return {
+ type: "color",
+ color: "katex-" + context.funcName.slice(1),
+ value: atoms,
+ };
+});
+
+// There are 2 flags for operators; whether they produce limits in
+// displaystyle, and whether they are symbols and should grow in
+// displaystyle. These four groups cover the four possible choices.
+
+// No limits, not symbols
+defineFunction([
+ "\\arcsin", "\\arccos", "\\arctan", "\\arg", "\\cos", "\\cosh",
+ "\\cot", "\\coth", "\\csc", "\\deg", "\\dim", "\\exp", "\\hom",
+ "\\ker", "\\lg", "\\ln", "\\log", "\\sec", "\\sin", "\\sinh",
+ "\\tan", "\\tanh",
+], {
+ numArgs: 0,
+}, function(context) {
+ return {
+ type: "op",
+ limits: false,
+ symbol: false,
+ body: context.funcName,
+ };
+});
+
+// Limits, not symbols
+defineFunction([
+ "\\det", "\\gcd", "\\inf", "\\lim", "\\liminf", "\\limsup", "\\max",
+ "\\min", "\\Pr", "\\sup",
+], {
+ numArgs: 0,
+}, function(context) {
+ return {
+ type: "op",
+ limits: true,
+ symbol: false,
+ body: context.funcName,
+ };
+});
+
+// No limits, symbols
+defineFunction([
+ "\\int", "\\iint", "\\iiint", "\\oint",
+], {
+ numArgs: 0,
+}, function(context) {
+ return {
+ type: "op",
+ limits: false,
+ symbol: true,
+ body: context.funcName,
+ };
+});
+
+// Limits, symbols
+defineFunction([
+ "\\coprod", "\\bigvee", "\\bigwedge", "\\biguplus", "\\bigcap",
+ "\\bigcup", "\\intop", "\\prod", "\\sum", "\\bigotimes",
+ "\\bigoplus", "\\bigodot", "\\bigsqcup", "\\smallint",
+], {
+ numArgs: 0,
+}, function(context) {
+ return {
+ type: "op",
+ limits: true,
+ symbol: true,
+ body: context.funcName,
+ };
+});
+
+// Fractions
+defineFunction([
+ "\\dfrac", "\\frac", "\\tfrac",
+ "\\dbinom", "\\binom", "\\tbinom",
+], {
+ numArgs: 2,
+ greediness: 2,
+}, function(context, args) {
+ var numer = args[0];
+ var denom = args[1];
+ var hasBarLine;
+ var leftDelim = null;
+ var rightDelim = null;
+ var size = "auto";
+
+ switch (context.funcName) {
+ case "\\dfrac":
+ case "\\frac":
+ case "\\tfrac":
+ hasBarLine = true;
+ break;
+ case "\\dbinom":
+ case "\\binom":
+ case "\\tbinom":
+ hasBarLine = false;
+ leftDelim = "(";
+ rightDelim = ")";
+ break;
+ default:
+ throw new Error("Unrecognized genfrac command");
+ }
+
+ switch (context.funcName) {
+ case "\\dfrac":
+ case "\\dbinom":
+ size = "display";
+ break;
+ case "\\tfrac":
+ case "\\tbinom":
+ size = "text";
+ break;
+ }
+
+ return {
+ type: "genfrac",
+ numer: numer,
+ denom: denom,
+ hasBarLine: hasBarLine,
+ leftDelim: leftDelim,
+ rightDelim: rightDelim,
+ size: size,
+ };
+});
+
+// Left and right overlap functions
+defineFunction(["\\llap", "\\rlap"], {
+ numArgs: 1,
+ allowedInText: true,
+}, function(context, args) {
+ var body = args[0];
+ return {
+ type: context.funcName.slice(1),
+ body: body,
+ };
+});
+
+// Delimiter functions
+defineFunction([
+ "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
+ "\\bigr", "\\Bigr", "\\biggr", "\\Biggr",
+ "\\bigm", "\\Bigm", "\\biggm", "\\Biggm",
+ "\\big", "\\Big", "\\bigg", "\\Bigg",
+ "\\left", "\\right",
+], {
+ numArgs: 1,
+}, function(context, args) {
+ var delim = args[0];
+ if (!utils.contains(delimiters, delim.value)) {
+ throw new ParseError(
+ "Invalid delimiter: '" + delim.value + "' after '" +
+ context.funcName + "'", delim);
+ }
+
+ // \left and \right are caught somewhere in Parser.js, which is
+ // why this data doesn't match what is in buildHTML.
+ if (context.funcName === "\\left" || context.funcName === "\\right") {
+ return {
+ type: "leftright",
+ value: delim.value,
+ };
+ } else {
+ return {
+ type: "delimsizing",
+ size: delimiterSizes[context.funcName].size,
+ delimType: delimiterSizes[context.funcName].type,
+ value: delim.value,
+ };
+ }
+});
+
+// Sizing functions (handled in Parser.js explicitly, hence no handler)
+defineFunction([
+ "\\tiny", "\\scriptsize", "\\footnotesize", "\\small",
+ "\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge",
+], 0, null);
+
+// Style changing functions (handled in Parser.js explicitly, hence no
+// handler)
+defineFunction([
+ "\\displaystyle", "\\textstyle", "\\scriptstyle",
+ "\\scriptscriptstyle",
+], 0, null);
+
+defineFunction([
+ // styles
+ "\\mathrm", "\\mathit", "\\mathbf",
+
+ // families
+ "\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf",
+ "\\mathtt",
+
+ // aliases
+ "\\Bbb", "\\bold", "\\frak",
+], {
+ numArgs: 1,
+ greediness: 2,
+}, function(context, args) {
+ var body = args[0];
+ var func = context.funcName;
+ if (func in fontAliases) {
+ func = fontAliases[func];
+ }
+ return {
+ type: "font",
+ font: func.slice(1),
+ body: body,
+ };
+});
+
+// Accents
+defineFunction([
+ "\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve",
+ "\\check", "\\hat", "\\vec", "\\dot",
+ // We don't support expanding accents yet
+ // "\\widetilde", "\\widehat"
+], {
+ numArgs: 1,
+}, function(context, args) {
+ var base = args[0];
+ return {
+ type: "accent",
+ accent: context.funcName,
+ base: base,
+ };
+});
+
+// Infix generalized fractions
+defineFunction(["\\over", "\\choose"], {
+ numArgs: 0,
+ infix: true,
+}, function(context) {
+ var replaceWith;
+ switch (context.funcName) {
+ case "\\over":
+ replaceWith = "\\frac";
+ break;
+ case "\\choose":
+ replaceWith = "\\binom";
+ break;
+ default:
+ throw new Error("Unrecognized infix genfrac command");
+ }
+ return {
+ type: "infix",
+ replaceWith: replaceWith,
+ token: context.token,
+ };
+});
+
+// Row breaks for aligned data
+defineFunction(["\\\\", "\\cr"], {
+ numArgs: 0,
+ numOptionalArgs: 1,
+ argTypes: ["size"],
+}, function(context, args) {
+ var size = args[0];
+ return {
+ type: "cr",
+ size: size,
+ };
+});
+
+// Environment delimiters
+defineFunction(["\\begin", "\\end"], {
+ numArgs: 1,
+ argTypes: ["text"],
+}, function(context, args) {
+ var nameGroup = args[0];
+ if (nameGroup.type !== "ordgroup") {
+ throw new ParseError("Invalid environment name", nameGroup);
+ }
+ var name = "";
+ for (var i = 0; i < nameGroup.value.length; ++i) {
+ name += nameGroup.value[i].value;
+ }
+ return {
+ type: "environment",
+ name: name,
+ nameGroup: nameGroup,
+ };
+});
+
+},{"./ParseError":6,"./utils":25}],20:[function(require,module,exports){
+/**
+ * These objects store data about MathML nodes. This is the MathML equivalent
+ * of the types in domTree.js. Since MathML handles its own rendering, and
+ * since we're mainly using MathML to improve accessibility, we don't manage
+ * any of the styling state that the plain DOM nodes do.
+ *
+ * The `toNode` and `toMarkup` functions work simlarly to how they do in
+ * domTree.js, creating namespaced DOM nodes and HTML text markup respectively.
+ */
+
+var utils = require("./utils");
+
+/**
+ * This node represents a general purpose MathML node of any type. The
+ * constructor requires the type of node to create (for example, `"mo"` or
+ * `"mspace"`, corresponding to `<mo>` and `<mspace>` tags).
+ */
+function MathNode(type, children) {
+ this.type = type;
+ this.attributes = {};
+ this.children = children || [];
+}
+
+/**
+ * Sets an attribute on a MathML node. MathML depends on attributes to convey a
+ * semantic content, so this is used heavily.
+ */
+MathNode.prototype.setAttribute = function(name, value) {
+ this.attributes[name] = value;
+};
+
+/**
+ * Converts the math node into a MathML-namespaced DOM element.
+ */
+MathNode.prototype.toNode = function() {
+ var node = document.createElementNS(
+ "http://www.w3.org/1998/Math/MathML", this.type);
+
+ for (var attr in this.attributes) {
+ if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
+ node.setAttribute(attr, this.attributes[attr]);
+ }
+ }
+
+ for (var i = 0; i < this.children.length; i++) {
+ node.appendChild(this.children[i].toNode());
+ }
+
+ return node;
+};
+
+/**
+ * Converts the math node into an HTML markup string.
+ */
+MathNode.prototype.toMarkup = function() {
+ var markup = "<" + this.type;
+
+ // Add the attributes
+ for (var attr in this.attributes) {
+ if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
+ markup += " " + attr + "=\"";
+ markup += utils.escape(this.attributes[attr]);
+ markup += "\"";
+ }
+ }
+
+ markup += ">";
+
+ for (var i = 0; i < this.children.length; i++) {
+ markup += this.children[i].toMarkup();
+ }
+
+ markup += "</" + this.type + ">";
+
+ return markup;
+};
+
+/**
+ * This node represents a piece of text.
+ */
+function TextNode(text) {
+ this.text = text;
+}
+
+/**
+ * Converts the text node into a DOM text node.
+ */
+TextNode.prototype.toNode = function() {
+ return document.createTextNode(this.text);
+};
+
+/**
+ * Converts the text node into HTML markup (which is just the text itself).
+ */
+TextNode.prototype.toMarkup = function() {
+ return utils.escape(this.text);
+};
+
+module.exports = {
+ MathNode: MathNode,
+ TextNode: TextNode,
+};
+
+},{"./utils":25}],21:[function(require,module,exports){
+/**
+ * The resulting parse tree nodes of the parse tree.
+ *
+ * It is possible to provide position information, so that a ParseNode can
+ * fulfil a role similar to a Token in error reporting.
+ * For details on the corresponding properties see Token constructor.
+ * Providing such information can lead to better error reporting.
+ *
+ * @param {string} type type of node, like e.g. "ordgroup"
+ * @param {?object} value type-specific representation of the node
+ * @param {string} mode parse mode in action for this node,
+ * "math" or "text"
+ * @param {Token=} firstToken first token of the input for this node,
+ * will omit position information if unset
+ * @param {Token=} lastToken last token of the input for this node,
+ * will default to firstToken if unset
+ */
+function ParseNode(type, value, mode, firstToken, lastToken) {
+ this.type = type;
+ this.value = value;
+ this.mode = mode;
+ if (firstToken && (!lastToken || lastToken.lexer === firstToken.lexer)) {
+ this.lexer = firstToken.lexer;
+ this.start = firstToken.start;
+ this.end = (lastToken || firstToken).end;
+ }
+}
+
+module.exports = {
+ ParseNode: ParseNode,
+};
+
+
+},{}],22:[function(require,module,exports){
+/**
+ * Provides a single function for parsing an expression using a Parser
+ * TODO(emily): Remove this
+ */
+
+var Parser = require("./Parser");
+
+/**
+ * Parses an expression using a Parser, then returns the parsed result.
+ */
+var parseTree = function(toParse, settings) {
+ if (!(typeof toParse === 'string' || toParse instanceof String)) {
+ throw new TypeError('KaTeX can only parse string typed expression');
+ }
+ var parser = new Parser(toParse, settings);
+
+ return parser.parse();
+};
+
+module.exports = parseTree;
+
+},{"./Parser":7}],23:[function(require,module,exports){
+/**
+ * This file holds a list of all no-argument functions and single-character
+ * symbols (like 'a' or ';').
+ *
+ * For each of the symbols, there are three properties they can have:
+ * - font (required): the font to be used for this symbol. Either "main" (the
+ normal font), or "ams" (the ams fonts).
+ * - group (required): the ParseNode group type the symbol should have (i.e.
+ "textord", "mathord", etc).
+ See https://github.com/Khan/KaTeX/wiki/Examining-TeX#group-types
+ * - replace: the character that this symbol or function should be
+ * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi
+ * character in the main font).
+ *
+ * The outermost map in the table indicates what mode the symbols should be
+ * accepted in (e.g. "math" or "text").
+ */
+
+module.exports = {
+ math: {},
+ text: {},
+};
+
+function defineSymbol(mode, font, group, replace, name) {
+ module.exports[mode][name] = {
+ font: font,
+ group: group,
+ replace: replace,
+ };
+}
+
+// Some abbreviations for commonly used strings.
+// This helps minify the code, and also spotting typos using jshint.
+
+// modes:
+var math = "math";
+var text = "text";
+
+// fonts:
+var main = "main";
+var ams = "ams";
+
+// groups:
+var accent = "accent";
+var bin = "bin";
+var close = "close";
+var inner = "inner";
+var mathord = "mathord";
+var op = "op";
+var open = "open";
+var punct = "punct";
+var rel = "rel";
+var spacing = "spacing";
+var textord = "textord";
+
+// Now comes the symbol table
+
+// Relation Symbols
+defineSymbol(math, main, rel, "\u2261", "\\equiv");
+defineSymbol(math, main, rel, "\u227a", "\\prec");
+defineSymbol(math, main, rel, "\u227b", "\\succ");
+defineSymbol(math, main, rel, "\u223c", "\\sim");
+defineSymbol(math, main, rel, "\u22a5", "\\perp");
+defineSymbol(math, main, rel, "\u2aaf", "\\preceq");
+defineSymbol(math, main, rel, "\u2ab0", "\\succeq");
+defineSymbol(math, main, rel, "\u2243", "\\simeq");
+defineSymbol(math, main, rel, "\u2223", "\\mid");
+defineSymbol(math, main, rel, "\u226a", "\\ll");
+defineSymbol(math, main, rel, "\u226b", "\\gg");
+defineSymbol(math, main, rel, "\u224d", "\\asymp");
+defineSymbol(math, main, rel, "\u2225", "\\parallel");
+defineSymbol(math, main, rel, "\u22c8", "\\bowtie");
+defineSymbol(math, main, rel, "\u2323", "\\smile");
+defineSymbol(math, main, rel, "\u2291", "\\sqsubseteq");
+defineSymbol(math, main, rel, "\u2292", "\\sqsupseteq");
+defineSymbol(math, main, rel, "\u2250", "\\doteq");
+defineSymbol(math, main, rel, "\u2322", "\\frown");
+defineSymbol(math, main, rel, "\u220b", "\\ni");
+defineSymbol(math, main, rel, "\u221d", "\\propto");
+defineSymbol(math, main, rel, "\u22a2", "\\vdash");
+defineSymbol(math, main, rel, "\u22a3", "\\dashv");
+defineSymbol(math, main, rel, "\u220b", "\\owns");
+
+// Punctuation
+defineSymbol(math, main, punct, "\u002e", "\\ldotp");
+defineSymbol(math, main, punct, "\u22c5", "\\cdotp");
+
+// Misc Symbols
+defineSymbol(math, main, textord, "\u0023", "\\#");
+defineSymbol(math, main, textord, "\u0026", "\\&");
+defineSymbol(math, main, textord, "\u2135", "\\aleph");
+defineSymbol(math, main, textord, "\u2200", "\\forall");
+defineSymbol(math, main, textord, "\u210f", "\\hbar");
+defineSymbol(math, main, textord, "\u2203", "\\exists");
+defineSymbol(math, main, textord, "\u2207", "\\nabla");
+defineSymbol(math, main, textord, "\u266d", "\\flat");
+defineSymbol(math, main, textord, "\u2113", "\\ell");
+defineSymbol(math, main, textord, "\u266e", "\\natural");
+defineSymbol(math, main, textord, "\u2663", "\\clubsuit");
+defineSymbol(math, main, textord, "\u2118", "\\wp");
+defineSymbol(math, main, textord, "\u266f", "\\sharp");
+defineSymbol(math, main, textord, "\u2662", "\\diamondsuit");
+defineSymbol(math, main, textord, "\u211c", "\\Re");
+defineSymbol(math, main, textord, "\u2661", "\\heartsuit");
+defineSymbol(math, main, textord, "\u2111", "\\Im");
+defineSymbol(math, main, textord, "\u2660", "\\spadesuit");
+
+// Math and Text
+defineSymbol(math, main, textord, "\u2020", "\\dag");
+defineSymbol(math, main, textord, "\u2021", "\\ddag");
+
+// Large Delimiters
+defineSymbol(math, main, close, "\u23b1", "\\rmoustache");
+defineSymbol(math, main, open, "\u23b0", "\\lmoustache");
+defineSymbol(math, main, close, "\u27ef", "\\rgroup");
+defineSymbol(math, main, open, "\u27ee", "\\lgroup");
+
+// Binary Operators
+defineSymbol(math, main, bin, "\u2213", "\\mp");
+defineSymbol(math, main, bin, "\u2296", "\\ominus");
+defineSymbol(math, main, bin, "\u228e", "\\uplus");
+defineSymbol(math, main, bin, "\u2293", "\\sqcap");
+defineSymbol(math, main, bin, "\u2217", "\\ast");
+defineSymbol(math, main, bin, "\u2294", "\\sqcup");
+defineSymbol(math, main, bin, "\u25ef", "\\bigcirc");
+defineSymbol(math, main, bin, "\u2219", "\\bullet");
+defineSymbol(math, main, bin, "\u2021", "\\ddagger");
+defineSymbol(math, main, bin, "\u2240", "\\wr");
+defineSymbol(math, main, bin, "\u2a3f", "\\amalg");
+
+// Arrow Symbols
+defineSymbol(math, main, rel, "\u27f5", "\\longleftarrow");
+defineSymbol(math, main, rel, "\u21d0", "\\Leftarrow");
+defineSymbol(math, main, rel, "\u27f8", "\\Longleftarrow");
+defineSymbol(math, main, rel, "\u27f6", "\\longrightarrow");
+defineSymbol(math, main, rel, "\u21d2", "\\Rightarrow");
+defineSymbol(math, main, rel, "\u27f9", "\\Longrightarrow");
+defineSymbol(math, main, rel, "\u2194", "\\leftrightarrow");
+defineSymbol(math, main, rel, "\u27f7", "\\longleftrightarrow");
+defineSymbol(math, main, rel, "\u21d4", "\\Leftrightarrow");
+defineSymbol(math, main, rel, "\u27fa", "\\Longleftrightarrow");
+defineSymbol(math, main, rel, "\u21a6", "\\mapsto");
+defineSymbol(math, main, rel, "\u27fc", "\\longmapsto");
+defineSymbol(math, main, rel, "\u2197", "\\nearrow");
+defineSymbol(math, main, rel, "\u21a9", "\\hookleftarrow");
+defineSymbol(math, main, rel, "\u21aa", "\\hookrightarrow");
+defineSymbol(math, main, rel, "\u2198", "\\searrow");
+defineSymbol(math, main, rel, "\u21bc", "\\leftharpoonup");
+defineSymbol(math, main, rel, "\u21c0", "\\rightharpoonup");
+defineSymbol(math, main, rel, "\u2199", "\\swarrow");
+defineSymbol(math, main, rel, "\u21bd", "\\leftharpoondown");
+defineSymbol(math, main, rel, "\u21c1", "\\rightharpoondown");
+defineSymbol(math, main, rel, "\u2196", "\\nwarrow");
+defineSymbol(math, main, rel, "\u21cc", "\\rightleftharpoons");
+
+// AMS Negated Binary Relations
+defineSymbol(math, ams, rel, "\u226e", "\\nless");
+defineSymbol(math, ams, rel, "\ue010", "\\nleqslant");
+defineSymbol(math, ams, rel, "\ue011", "\\nleqq");
+defineSymbol(math, ams, rel, "\u2a87", "\\lneq");
+defineSymbol(math, ams, rel, "\u2268", "\\lneqq");
+defineSymbol(math, ams, rel, "\ue00c", "\\lvertneqq");
+defineSymbol(math, ams, rel, "\u22e6", "\\lnsim");
+defineSymbol(math, ams, rel, "\u2a89", "\\lnapprox");
+defineSymbol(math, ams, rel, "\u2280", "\\nprec");
+defineSymbol(math, ams, rel, "\u22e0", "\\npreceq");
+defineSymbol(math, ams, rel, "\u22e8", "\\precnsim");
+defineSymbol(math, ams, rel, "\u2ab9", "\\precnapprox");
+defineSymbol(math, ams, rel, "\u2241", "\\nsim");
+defineSymbol(math, ams, rel, "\ue006", "\\nshortmid");
+defineSymbol(math, ams, rel, "\u2224", "\\nmid");
+defineSymbol(math, ams, rel, "\u22ac", "\\nvdash");
+defineSymbol(math, ams, rel, "\u22ad", "\\nvDash");
+defineSymbol(math, ams, rel, "\u22ea", "\\ntriangleleft");
+defineSymbol(math, ams, rel, "\u22ec", "\\ntrianglelefteq");
+defineSymbol(math, ams, rel, "\u228a", "\\subsetneq");
+defineSymbol(math, ams, rel, "\ue01a", "\\varsubsetneq");
+defineSymbol(math, ams, rel, "\u2acb", "\\subsetneqq");
+defineSymbol(math, ams, rel, "\ue017", "\\varsubsetneqq");
+defineSymbol(math, ams, rel, "\u226f", "\\ngtr");
+defineSymbol(math, ams, rel, "\ue00f", "\\ngeqslant");
+defineSymbol(math, ams, rel, "\ue00e", "\\ngeqq");
+defineSymbol(math, ams, rel, "\u2a88", "\\gneq");
+defineSymbol(math, ams, rel, "\u2269", "\\gneqq");
+defineSymbol(math, ams, rel, "\ue00d", "\\gvertneqq");
+defineSymbol(math, ams, rel, "\u22e7", "\\gnsim");
+defineSymbol(math, ams, rel, "\u2a8a", "\\gnapprox");
+defineSymbol(math, ams, rel, "\u2281", "\\nsucc");
+defineSymbol(math, ams, rel, "\u22e1", "\\nsucceq");
+defineSymbol(math, ams, rel, "\u22e9", "\\succnsim");
+defineSymbol(math, ams, rel, "\u2aba", "\\succnapprox");
+defineSymbol(math, ams, rel, "\u2246", "\\ncong");
+defineSymbol(math, ams, rel, "\ue007", "\\nshortparallel");
+defineSymbol(math, ams, rel, "\u2226", "\\nparallel");
+defineSymbol(math, ams, rel, "\u22af", "\\nVDash");
+defineSymbol(math, ams, rel, "\u22eb", "\\ntriangleright");
+defineSymbol(math, ams, rel, "\u22ed", "\\ntrianglerighteq");
+defineSymbol(math, ams, rel, "\ue018", "\\nsupseteqq");
+defineSymbol(math, ams, rel, "\u228b", "\\supsetneq");
+defineSymbol(math, ams, rel, "\ue01b", "\\varsupsetneq");
+defineSymbol(math, ams, rel, "\u2acc", "\\supsetneqq");
+defineSymbol(math, ams, rel, "\ue019", "\\varsupsetneqq");
+defineSymbol(math, ams, rel, "\u22ae", "\\nVdash");
+defineSymbol(math, ams, rel, "\u2ab5", "\\precneqq");
+defineSymbol(math, ams, rel, "\u2ab6", "\\succneqq");
+defineSymbol(math, ams, rel, "\ue016", "\\nsubseteqq");
+defineSymbol(math, ams, bin, "\u22b4", "\\unlhd");
+defineSymbol(math, ams, bin, "\u22b5", "\\unrhd");
+
+// AMS Negated Arrows
+defineSymbol(math, ams, rel, "\u219a", "\\nleftarrow");
+defineSymbol(math, ams, rel, "\u219b", "\\nrightarrow");
+defineSymbol(math, ams, rel, "\u21cd", "\\nLeftarrow");
+defineSymbol(math, ams, rel, "\u21cf", "\\nRightarrow");
+defineSymbol(math, ams, rel, "\u21ae", "\\nleftrightarrow");
+defineSymbol(math, ams, rel, "\u21ce", "\\nLeftrightarrow");
+
+// AMS Misc
+defineSymbol(math, ams, rel, "\u25b3", "\\vartriangle");
+defineSymbol(math, ams, textord, "\u210f", "\\hslash");
+defineSymbol(math, ams, textord, "\u25bd", "\\triangledown");
+defineSymbol(math, ams, textord, "\u25ca", "\\lozenge");
+defineSymbol(math, ams, textord, "\u24c8", "\\circledS");
+defineSymbol(math, ams, textord, "\u00ae", "\\circledR");
+defineSymbol(math, ams, textord, "\u2221", "\\measuredangle");
+defineSymbol(math, ams, textord, "\u2204", "\\nexists");
+defineSymbol(math, ams, textord, "\u2127", "\\mho");
+defineSymbol(math, ams, textord, "\u2132", "\\Finv");
+defineSymbol(math, ams, textord, "\u2141", "\\Game");
+defineSymbol(math, ams, textord, "\u006b", "\\Bbbk");
+defineSymbol(math, ams, textord, "\u2035", "\\backprime");
+defineSymbol(math, ams, textord, "\u25b2", "\\blacktriangle");
+defineSymbol(math, ams, textord, "\u25bc", "\\blacktriangledown");
+defineSymbol(math, ams, textord, "\u25a0", "\\blacksquare");
+defineSymbol(math, ams, textord, "\u29eb", "\\blacklozenge");
+defineSymbol(math, ams, textord, "\u2605", "\\bigstar");
+defineSymbol(math, ams, textord, "\u2222", "\\sphericalangle");
+defineSymbol(math, ams, textord, "\u2201", "\\complement");
+defineSymbol(math, ams, textord, "\u00f0", "\\eth");
+defineSymbol(math, ams, textord, "\u2571", "\\diagup");
+defineSymbol(math, ams, textord, "\u2572", "\\diagdown");
+defineSymbol(math, ams, textord, "\u25a1", "\\square");
+defineSymbol(math, ams, textord, "\u25a1", "\\Box");
+defineSymbol(math, ams, textord, "\u25ca", "\\Diamond");
+defineSymbol(math, ams, textord, "\u00a5", "\\yen");
+defineSymbol(math, ams, textord, "\u2713", "\\checkmark");
+
+// AMS Hebrew
+defineSymbol(math, ams, textord, "\u2136", "\\beth");
+defineSymbol(math, ams, textord, "\u2138", "\\daleth");
+defineSymbol(math, ams, textord, "\u2137", "\\gimel");
+
+// AMS Greek
+defineSymbol(math, ams, textord, "\u03dd", "\\digamma");
+defineSymbol(math, ams, textord, "\u03f0", "\\varkappa");
+
+// AMS Delimiters
+defineSymbol(math, ams, open, "\u250c", "\\ulcorner");
+defineSymbol(math, ams, close, "\u2510", "\\urcorner");
+defineSymbol(math, ams, open, "\u2514", "\\llcorner");
+defineSymbol(math, ams, close, "\u2518", "\\lrcorner");
+
+// AMS Binary Relations
+defineSymbol(math, ams, rel, "\u2266", "\\leqq");
+defineSymbol(math, ams, rel, "\u2a7d", "\\leqslant");
+defineSymbol(math, ams, rel, "\u2a95", "\\eqslantless");
+defineSymbol(math, ams, rel, "\u2272", "\\lesssim");
+defineSymbol(math, ams, rel, "\u2a85", "\\lessapprox");
+defineSymbol(math, ams, rel, "\u224a", "\\approxeq");
+defineSymbol(math, ams, bin, "\u22d6", "\\lessdot");
+defineSymbol(math, ams, rel, "\u22d8", "\\lll");
+defineSymbol(math, ams, rel, "\u2276", "\\lessgtr");
+defineSymbol(math, ams, rel, "\u22da", "\\lesseqgtr");
+defineSymbol(math, ams, rel, "\u2a8b", "\\lesseqqgtr");
+defineSymbol(math, ams, rel, "\u2251", "\\doteqdot");
+defineSymbol(math, ams, rel, "\u2253", "\\risingdotseq");
+defineSymbol(math, ams, rel, "\u2252", "\\fallingdotseq");
+defineSymbol(math, ams, rel, "\u223d", "\\backsim");
+defineSymbol(math, ams, rel, "\u22cd", "\\backsimeq");
+defineSymbol(math, ams, rel, "\u2ac5", "\\subseteqq");
+defineSymbol(math, ams, rel, "\u22d0", "\\Subset");
+defineSymbol(math, ams, rel, "\u228f", "\\sqsubset");
+defineSymbol(math, ams, rel, "\u227c", "\\preccurlyeq");
+defineSymbol(math, ams, rel, "\u22de", "\\curlyeqprec");
+defineSymbol(math, ams, rel, "\u227e", "\\precsim");
+defineSymbol(math, ams, rel, "\u2ab7", "\\precapprox");
+defineSymbol(math, ams, rel, "\u22b2", "\\vartriangleleft");
+defineSymbol(math, ams, rel, "\u22b4", "\\trianglelefteq");
+defineSymbol(math, ams, rel, "\u22a8", "\\vDash");
+defineSymbol(math, ams, rel, "\u22aa", "\\Vvdash");
+defineSymbol(math, ams, rel, "\u2323", "\\smallsmile");
+defineSymbol(math, ams, rel, "\u2322", "\\smallfrown");
+defineSymbol(math, ams, rel, "\u224f", "\\bumpeq");
+defineSymbol(math, ams, rel, "\u224e", "\\Bumpeq");
+defineSymbol(math, ams, rel, "\u2267", "\\geqq");
+defineSymbol(math, ams, rel, "\u2a7e", "\\geqslant");
+defineSymbol(math, ams, rel, "\u2a96", "\\eqslantgtr");
+defineSymbol(math, ams, rel, "\u2273", "\\gtrsim");
+defineSymbol(math, ams, rel, "\u2a86", "\\gtrapprox");
+defineSymbol(math, ams, bin, "\u22d7", "\\gtrdot");
+defineSymbol(math, ams, rel, "\u22d9", "\\ggg");
+defineSymbol(math, ams, rel, "\u2277", "\\gtrless");
+defineSymbol(math, ams, rel, "\u22db", "\\gtreqless");
+defineSymbol(math, ams, rel, "\u2a8c", "\\gtreqqless");
+defineSymbol(math, ams, rel, "\u2256", "\\eqcirc");
+defineSymbol(math, ams, rel, "\u2257", "\\circeq");
+defineSymbol(math, ams, rel, "\u225c", "\\triangleq");
+defineSymbol(math, ams, rel, "\u223c", "\\thicksim");
+defineSymbol(math, ams, rel, "\u2248", "\\thickapprox");
+defineSymbol(math, ams, rel, "\u2ac6", "\\supseteqq");
+defineSymbol(math, ams, rel, "\u22d1", "\\Supset");
+defineSymbol(math, ams, rel, "\u2290", "\\sqsupset");
+defineSymbol(math, ams, rel, "\u227d", "\\succcurlyeq");
+defineSymbol(math, ams, rel, "\u22df", "\\curlyeqsucc");
+defineSymbol(math, ams, rel, "\u227f", "\\succsim");
+defineSymbol(math, ams, rel, "\u2ab8", "\\succapprox");
+defineSymbol(math, ams, rel, "\u22b3", "\\vartriangleright");
+defineSymbol(math, ams, rel, "\u22b5", "\\trianglerighteq");
+defineSymbol(math, ams, rel, "\u22a9", "\\Vdash");
+defineSymbol(math, ams, rel, "\u2223", "\\shortmid");
+defineSymbol(math, ams, rel, "\u2225", "\\shortparallel");
+defineSymbol(math, ams, rel, "\u226c", "\\between");
+defineSymbol(math, ams, rel, "\u22d4", "\\pitchfork");
+defineSymbol(math, ams, rel, "\u221d", "\\varpropto");
+defineSymbol(math, ams, rel, "\u25c0", "\\blacktriangleleft");
+defineSymbol(math, ams, rel, "\u2234", "\\therefore");
+defineSymbol(math, ams, rel, "\u220d", "\\backepsilon");
+defineSymbol(math, ams, rel, "\u25b6", "\\blacktriangleright");
+defineSymbol(math, ams, rel, "\u2235", "\\because");
+defineSymbol(math, ams, rel, "\u22d8", "\\llless");
+defineSymbol(math, ams, rel, "\u22d9", "\\gggtr");
+defineSymbol(math, ams, bin, "\u22b2", "\\lhd");
+defineSymbol(math, ams, bin, "\u22b3", "\\rhd");
+defineSymbol(math, ams, rel, "\u2242", "\\eqsim");
+defineSymbol(math, main, rel, "\u22c8", "\\Join");
+defineSymbol(math, ams, rel, "\u2251", "\\Doteq");
+
+// AMS Binary Operators
+defineSymbol(math, ams, bin, "\u2214", "\\dotplus");
+defineSymbol(math, ams, bin, "\u2216", "\\smallsetminus");
+defineSymbol(math, ams, bin, "\u22d2", "\\Cap");
+defineSymbol(math, ams, bin, "\u22d3", "\\Cup");
+defineSymbol(math, ams, bin, "\u2a5e", "\\doublebarwedge");
+defineSymbol(math, ams, bin, "\u229f", "\\boxminus");
+defineSymbol(math, ams, bin, "\u229e", "\\boxplus");
+defineSymbol(math, ams, bin, "\u22c7", "\\divideontimes");
+defineSymbol(math, ams, bin, "\u22c9", "\\ltimes");
+defineSymbol(math, ams, bin, "\u22ca", "\\rtimes");
+defineSymbol(math, ams, bin, "\u22cb", "\\leftthreetimes");
+defineSymbol(math, ams, bin, "\u22cc", "\\rightthreetimes");
+defineSymbol(math, ams, bin, "\u22cf", "\\curlywedge");
+defineSymbol(math, ams, bin, "\u22ce", "\\curlyvee");
+defineSymbol(math, ams, bin, "\u229d", "\\circleddash");
+defineSymbol(math, ams, bin, "\u229b", "\\circledast");
+defineSymbol(math, ams, bin, "\u22c5", "\\centerdot");
+defineSymbol(math, ams, bin, "\u22ba", "\\intercal");
+defineSymbol(math, ams, bin, "\u22d2", "\\doublecap");
+defineSymbol(math, ams, bin, "\u22d3", "\\doublecup");
+defineSymbol(math, ams, bin, "\u22a0", "\\boxtimes");
+
+// AMS Arrows
+defineSymbol(math, ams, rel, "\u21e2", "\\dashrightarrow");
+defineSymbol(math, ams, rel, "\u21e0", "\\dashleftarrow");
+defineSymbol(math, ams, rel, "\u21c7", "\\leftleftarrows");
+defineSymbol(math, ams, rel, "\u21c6", "\\leftrightarrows");
+defineSymbol(math, ams, rel, "\u21da", "\\Lleftarrow");
+defineSymbol(math, ams, rel, "\u219e", "\\twoheadleftarrow");
+defineSymbol(math, ams, rel, "\u21a2", "\\leftarrowtail");
+defineSymbol(math, ams, rel, "\u21ab", "\\looparrowleft");
+defineSymbol(math, ams, rel, "\u21cb", "\\leftrightharpoons");
+defineSymbol(math, ams, rel, "\u21b6", "\\curvearrowleft");
+defineSymbol(math, ams, rel, "\u21ba", "\\circlearrowleft");
+defineSymbol(math, ams, rel, "\u21b0", "\\Lsh");
+defineSymbol(math, ams, rel, "\u21c8", "\\upuparrows");
+defineSymbol(math, ams, rel, "\u21bf", "\\upharpoonleft");
+defineSymbol(math, ams, rel, "\u21c3", "\\downharpoonleft");
+defineSymbol(math, ams, rel, "\u22b8", "\\multimap");
+defineSymbol(math, ams, rel, "\u21ad", "\\leftrightsquigarrow");
+defineSymbol(math, ams, rel, "\u21c9", "\\rightrightarrows");
+defineSymbol(math, ams, rel, "\u21c4", "\\rightleftarrows");
+defineSymbol(math, ams, rel, "\u21a0", "\\twoheadrightarrow");
+defineSymbol(math, ams, rel, "\u21a3", "\\rightarrowtail");
+defineSymbol(math, ams, rel, "\u21ac", "\\looparrowright");
+defineSymbol(math, ams, rel, "\u21b7", "\\curvearrowright");
+defineSymbol(math, ams, rel, "\u21bb", "\\circlearrowright");
+defineSymbol(math, ams, rel, "\u21b1", "\\Rsh");
+defineSymbol(math, ams, rel, "\u21ca", "\\downdownarrows");
+defineSymbol(math, ams, rel, "\u21be", "\\upharpoonright");
+defineSymbol(math, ams, rel, "\u21c2", "\\downharpoonright");
+defineSymbol(math, ams, rel, "\u21dd", "\\rightsquigarrow");
+defineSymbol(math, ams, rel, "\u21dd", "\\leadsto");
+defineSymbol(math, ams, rel, "\u21db", "\\Rrightarrow");
+defineSymbol(math, ams, rel, "\u21be", "\\restriction");
+
+defineSymbol(math, main, textord, "\u2018", "`");
+defineSymbol(math, main, textord, "$", "\\$");
+defineSymbol(math, main, textord, "%", "\\%");
+defineSymbol(math, main, textord, "_", "\\_");
+defineSymbol(math, main, textord, "\u2220", "\\angle");
+defineSymbol(math, main, textord, "\u221e", "\\infty");
+defineSymbol(math, main, textord, "\u2032", "\\prime");
+defineSymbol(math, main, textord, "\u25b3", "\\triangle");
+defineSymbol(math, main, textord, "\u0393", "\\Gamma");
+defineSymbol(math, main, textord, "\u0394", "\\Delta");
+defineSymbol(math, main, textord, "\u0398", "\\Theta");
+defineSymbol(math, main, textord, "\u039b", "\\Lambda");
+defineSymbol(math, main, textord, "\u039e", "\\Xi");
+defineSymbol(math, main, textord, "\u03a0", "\\Pi");
+defineSymbol(math, main, textord, "\u03a3", "\\Sigma");
+defineSymbol(math, main, textord, "\u03a5", "\\Upsilon");
+defineSymbol(math, main, textord, "\u03a6", "\\Phi");
+defineSymbol(math, main, textord, "\u03a8", "\\Psi");
+defineSymbol(math, main, textord, "\u03a9", "\\Omega");
+defineSymbol(math, main, textord, "\u00ac", "\\neg");
+defineSymbol(math, main, textord, "\u00ac", "\\lnot");
+defineSymbol(math, main, textord, "\u22a4", "\\top");
+defineSymbol(math, main, textord, "\u22a5", "\\bot");
+defineSymbol(math, main, textord, "\u2205", "\\emptyset");
+defineSymbol(math, ams, textord, "\u2205", "\\varnothing");
+defineSymbol(math, main, mathord, "\u03b1", "\\alpha");
+defineSymbol(math, main, mathord, "\u03b2", "\\beta");
+defineSymbol(math, main, mathord, "\u03b3", "\\gamma");
+defineSymbol(math, main, mathord, "\u03b4", "\\delta");
+defineSymbol(math, main, mathord, "\u03f5", "\\epsilon");
+defineSymbol(math, main, mathord, "\u03b6", "\\zeta");
+defineSymbol(math, main, mathord, "\u03b7", "\\eta");
+defineSymbol(math, main, mathord, "\u03b8", "\\theta");
+defineSymbol(math, main, mathord, "\u03b9", "\\iota");
+defineSymbol(math, main, mathord, "\u03ba", "\\kappa");
+defineSymbol(math, main, mathord, "\u03bb", "\\lambda");
+defineSymbol(math, main, mathord, "\u03bc", "\\mu");
+defineSymbol(math, main, mathord, "\u03bd", "\\nu");
+defineSymbol(math, main, mathord, "\u03be", "\\xi");
+defineSymbol(math, main, mathord, "o", "\\omicron");
+defineSymbol(math, main, mathord, "\u03c0", "\\pi");
+defineSymbol(math, main, mathord, "\u03c1", "\\rho");
+defineSymbol(math, main, mathord, "\u03c3", "\\sigma");
+defineSymbol(math, main, mathord, "\u03c4", "\\tau");
+defineSymbol(math, main, mathord, "\u03c5", "\\upsilon");
+defineSymbol(math, main, mathord, "\u03d5", "\\phi");
+defineSymbol(math, main, mathord, "\u03c7", "\\chi");
+defineSymbol(math, main, mathord, "\u03c8", "\\psi");
+defineSymbol(math, main, mathord, "\u03c9", "\\omega");
+defineSymbol(math, main, mathord, "\u03b5", "\\varepsilon");
+defineSymbol(math, main, mathord, "\u03d1", "\\vartheta");
+defineSymbol(math, main, mathord, "\u03d6", "\\varpi");
+defineSymbol(math, main, mathord, "\u03f1", "\\varrho");
+defineSymbol(math, main, mathord, "\u03c2", "\\varsigma");
+defineSymbol(math, main, mathord, "\u03c6", "\\varphi");
+defineSymbol(math, main, bin, "\u2217", "*");
+defineSymbol(math, main, bin, "+", "+");
+defineSymbol(math, main, bin, "\u2212", "-");
+defineSymbol(math, main, bin, "\u22c5", "\\cdot");
+defineSymbol(math, main, bin, "\u2218", "\\circ");
+defineSymbol(math, main, bin, "\u00f7", "\\div");
+defineSymbol(math, main, bin, "\u00b1", "\\pm");
+defineSymbol(math, main, bin, "\u00d7", "\\times");
+defineSymbol(math, main, bin, "\u2229", "\\cap");
+defineSymbol(math, main, bin, "\u222a", "\\cup");
+defineSymbol(math, main, bin, "\u2216", "\\setminus");
+defineSymbol(math, main, bin, "\u2227", "\\land");
+defineSymbol(math, main, bin, "\u2228", "\\lor");
+defineSymbol(math, main, bin, "\u2227", "\\wedge");
+defineSymbol(math, main, bin, "\u2228", "\\vee");
+defineSymbol(math, main, textord, "\u221a", "\\surd");
+defineSymbol(math, main, open, "(", "(");
+defineSymbol(math, main, open, "[", "[");
+defineSymbol(math, main, open, "\u27e8", "\\langle");
+defineSymbol(math, main, open, "\u2223", "\\lvert");
+defineSymbol(math, main, open, "\u2225", "\\lVert");
+defineSymbol(math, main, close, ")", ")");
+defineSymbol(math, main, close, "]", "]");
+defineSymbol(math, main, close, "?", "?");
+defineSymbol(math, main, close, "!", "!");
+defineSymbol(math, main, close, "\u27e9", "\\rangle");
+defineSymbol(math, main, close, "\u2223", "\\rvert");
+defineSymbol(math, main, close, "\u2225", "\\rVert");
+defineSymbol(math, main, rel, "=", "=");
+defineSymbol(math, main, rel, "<", "<");
+defineSymbol(math, main, rel, ">", ">");
+defineSymbol(math, main, rel, ":", ":");
+defineSymbol(math, main, rel, "\u2248", "\\approx");
+defineSymbol(math, main, rel, "\u2245", "\\cong");
+defineSymbol(math, main, rel, "\u2265", "\\ge");
+defineSymbol(math, main, rel, "\u2265", "\\geq");
+defineSymbol(math, main, rel, "\u2190", "\\gets");
+defineSymbol(math, main, rel, ">", "\\gt");
+defineSymbol(math, main, rel, "\u2208", "\\in");
+defineSymbol(math, main, rel, "\u2209", "\\notin");
+defineSymbol(math, main, rel, "\u2282", "\\subset");
+defineSymbol(math, main, rel, "\u2283", "\\supset");
+defineSymbol(math, main, rel, "\u2286", "\\subseteq");
+defineSymbol(math, main, rel, "\u2287", "\\supseteq");
+defineSymbol(math, ams, rel, "\u2288", "\\nsubseteq");
+defineSymbol(math, ams, rel, "\u2289", "\\nsupseteq");
+defineSymbol(math, main, rel, "\u22a8", "\\models");
+defineSymbol(math, main, rel, "\u2190", "\\leftarrow");
+defineSymbol(math, main, rel, "\u2264", "\\le");
+defineSymbol(math, main, rel, "\u2264", "\\leq");
+defineSymbol(math, main, rel, "<", "\\lt");
+defineSymbol(math, main, rel, "\u2260", "\\ne");
+defineSymbol(math, main, rel, "\u2260", "\\neq");
+defineSymbol(math, main, rel, "\u2192", "\\rightarrow");
+defineSymbol(math, main, rel, "\u2192", "\\to");
+defineSymbol(math, ams, rel, "\u2271", "\\ngeq");
+defineSymbol(math, ams, rel, "\u2270", "\\nleq");
+defineSymbol(math, main, spacing, null, "\\!");
+defineSymbol(math, main, spacing, "\u00a0", "\\ ");
+defineSymbol(math, main, spacing, "\u00a0", "~");
+defineSymbol(math, main, spacing, null, "\\,");
+defineSymbol(math, main, spacing, null, "\\:");
+defineSymbol(math, main, spacing, null, "\\;");
+defineSymbol(math, main, spacing, null, "\\enspace");
+defineSymbol(math, main, spacing, null, "\\qquad");
+defineSymbol(math, main, spacing, null, "\\quad");
+defineSymbol(math, main, spacing, "\u00a0", "\\space");
+defineSymbol(math, main, punct, ",", ",");
+defineSymbol(math, main, punct, ";", ";");
+defineSymbol(math, main, punct, ":", "\\colon");
+defineSymbol(math, ams, bin, "\u22bc", "\\barwedge");
+defineSymbol(math, ams, bin, "\u22bb", "\\veebar");
+defineSymbol(math, main, bin, "\u2299", "\\odot");
+defineSymbol(math, main, bin, "\u2295", "\\oplus");
+defineSymbol(math, main, bin, "\u2297", "\\otimes");
+defineSymbol(math, main, textord, "\u2202", "\\partial");
+defineSymbol(math, main, bin, "\u2298", "\\oslash");
+defineSymbol(math, ams, bin, "\u229a", "\\circledcirc");
+defineSymbol(math, ams, bin, "\u22a1", "\\boxdot");
+defineSymbol(math, main, bin, "\u25b3", "\\bigtriangleup");
+defineSymbol(math, main, bin, "\u25bd", "\\bigtriangledown");
+defineSymbol(math, main, bin, "\u2020", "\\dagger");
+defineSymbol(math, main, bin, "\u22c4", "\\diamond");
+defineSymbol(math, main, bin, "\u22c6", "\\star");
+defineSymbol(math, main, bin, "\u25c3", "\\triangleleft");
+defineSymbol(math, main, bin, "\u25b9", "\\triangleright");
+defineSymbol(math, main, open, "{", "\\{");
+defineSymbol(math, main, close, "}", "\\}");
+defineSymbol(math, main, open, "{", "\\lbrace");
+defineSymbol(math, main, close, "}", "\\rbrace");
+defineSymbol(math, main, open, "[", "\\lbrack");
+defineSymbol(math, main, close, "]", "\\rbrack");
+defineSymbol(math, main, open, "\u230a", "\\lfloor");
+defineSymbol(math, main, close, "\u230b", "\\rfloor");
+defineSymbol(math, main, open, "\u2308", "\\lceil");
+defineSymbol(math, main, close, "\u2309", "\\rceil");
+defineSymbol(math, main, textord, "\\", "\\backslash");
+defineSymbol(math, main, textord, "\u2223", "|");
+defineSymbol(math, main, textord, "\u2223", "\\vert");
+defineSymbol(math, main, textord, "\u2225", "\\|");
+defineSymbol(math, main, textord, "\u2225", "\\Vert");
+defineSymbol(math, main, rel, "\u2191", "\\uparrow");
+defineSymbol(math, main, rel, "\u21d1", "\\Uparrow");
+defineSymbol(math, main, rel, "\u2193", "\\downarrow");
+defineSymbol(math, main, rel, "\u21d3", "\\Downarrow");
+defineSymbol(math, main, rel, "\u2195", "\\updownarrow");
+defineSymbol(math, main, rel, "\u21d5", "\\Updownarrow");
+defineSymbol(math, math, op, "\u2210", "\\coprod");
+defineSymbol(math, math, op, "\u22c1", "\\bigvee");
+defineSymbol(math, math, op, "\u22c0", "\\bigwedge");
+defineSymbol(math, math, op, "\u2a04", "\\biguplus");
+defineSymbol(math, math, op, "\u22c2", "\\bigcap");
+defineSymbol(math, math, op, "\u22c3", "\\bigcup");
+defineSymbol(math, math, op, "\u222b", "\\int");
+defineSymbol(math, math, op, "\u222b", "\\intop");
+defineSymbol(math, math, op, "\u222c", "\\iint");
+defineSymbol(math, math, op, "\u222d", "\\iiint");
+defineSymbol(math, math, op, "\u220f", "\\prod");
+defineSymbol(math, math, op, "\u2211", "\\sum");
+defineSymbol(math, math, op, "\u2a02", "\\bigotimes");
+defineSymbol(math, math, op, "\u2a01", "\\bigoplus");
+defineSymbol(math, math, op, "\u2a00", "\\bigodot");
+defineSymbol(math, math, op, "\u222e", "\\oint");
+defineSymbol(math, math, op, "\u2a06", "\\bigsqcup");
+defineSymbol(math, math, op, "\u222b", "\\smallint");
+defineSymbol(math, main, inner, "\u2026", "\\ldots");
+defineSymbol(math, main, inner, "\u22ef", "\\cdots");
+defineSymbol(math, main, inner, "\u22f1", "\\ddots");
+defineSymbol(math, main, textord, "\u22ee", "\\vdots");
+defineSymbol(math, main, accent, "\u00b4", "\\acute");
+defineSymbol(math, main, accent, "\u0060", "\\grave");
+defineSymbol(math, main, accent, "\u00a8", "\\ddot");
+defineSymbol(math, main, accent, "\u007e", "\\tilde");
+defineSymbol(math, main, accent, "\u00af", "\\bar");
+defineSymbol(math, main, accent, "\u02d8", "\\breve");
+defineSymbol(math, main, accent, "\u02c7", "\\check");
+defineSymbol(math, main, accent, "\u005e", "\\hat");
+defineSymbol(math, main, accent, "\u20d7", "\\vec");
+defineSymbol(math, main, accent, "\u02d9", "\\dot");
+defineSymbol(math, main, mathord, "\u0131", "\\imath");
+defineSymbol(math, main, mathord, "\u0237", "\\jmath");
+
+defineSymbol(text, main, textord, "\u2013", "--");
+defineSymbol(text, main, textord, "\u2014", "---");
+defineSymbol(text, main, textord, "\u2018", "`");
+defineSymbol(text, main, textord, "\u2019", "'");
+defineSymbol(text, main, textord, "\u201c", "``");
+defineSymbol(text, main, textord, "\u201d", "''");
+defineSymbol(math, main, textord, "\u00b0", "\\degree");
+defineSymbol(text, main, textord, "\u00b0", "\\degree");
+defineSymbol(math, main, mathord, "\u00a3", "\\pounds");
+defineSymbol(math, ams, textord, "\u2720", "\\maltese");
+defineSymbol(text, ams, textord, "\u2720", "\\maltese");
+
+defineSymbol(text, main, spacing, "\u00a0", "\\ ");
+defineSymbol(text, main, spacing, "\u00a0", " ");
+defineSymbol(text, main, spacing, "\u00a0", "~");
+
+// There are lots of symbols which are the same, so we add them in afterwards.
+var i;
+var ch;
+
+// All of these are textords in math mode
+var mathTextSymbols = "0123456789/@.\"";
+for (i = 0; i < mathTextSymbols.length; i++) {
+ ch = mathTextSymbols.charAt(i);
+ defineSymbol(math, main, textord, ch, ch);
+}
+
+// All of these are textords in text mode
+var textSymbols = "0123456789!@*()-=+[]\";:?/.,";
+for (i = 0; i < textSymbols.length; i++) {
+ ch = textSymbols.charAt(i);
+ defineSymbol(text, main, textord, ch, ch);
+}
+
+// All of these are textords in text mode, and mathords in math mode
+var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+for (i = 0; i < letters.length; i++) {
+ ch = letters.charAt(i);
+ defineSymbol(math, main, mathord, ch, ch);
+ defineSymbol(text, main, textord, ch, ch);
+}
+
+// Latin-1 letters
+for (i = 0x00C0; i <= 0x00D6; i++) {
+ ch = String.fromCharCode(i);
+ defineSymbol(text, main, textord, ch, ch);
+}
+
+for (i = 0x00D8; i <= 0x00F6; i++) {
+ ch = String.fromCharCode(i);
+ defineSymbol(text, main, textord, ch, ch);
+}
+
+for (i = 0x00F8; i <= 0x00FF; i++) {
+ ch = String.fromCharCode(i);
+ defineSymbol(text, main, textord, ch, ch);
+}
+
+// Cyrillic
+for (i = 0x0410; i <= 0x044F; i++) {
+ ch = String.fromCharCode(i);
+ defineSymbol(text, main, textord, ch, ch);
+}
+
+},{}],24:[function(require,module,exports){
+var hangulRegex = /[\uAC00-\uD7AF]/;
+
+// This regex combines
+// - Hiragana: [\u3040-\u309F]
+// - Katakana: [\u30A0-\u30FF]
+// - CJK ideograms: [\u4E00-\u9FAF]
+// - Hangul syllables: [\uAC00-\uD7AF]
+// Notably missing are halfwidth Katakana and Romanji glyphs.
+var cjkRegex =
+ /[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FAF]|[\uAC00-\uD7AF]/;
+
+module.exports = {
+ cjkRegex: cjkRegex,
+ hangulRegex: hangulRegex,
+};
+
+},{}],25:[function(require,module,exports){
+/**
+ * This file contains a list of utility functions which are useful in other
+ * files.
+ */
+
+/**
+ * Provide an `indexOf` function which works in IE8, but defers to native if
+ * possible.
+ */
+var nativeIndexOf = Array.prototype.indexOf;
+var indexOf = function(list, elem) {
+ if (list == null) {
+ return -1;
+ }
+ if (nativeIndexOf && list.indexOf === nativeIndexOf) {
+ return list.indexOf(elem);
+ }
+ var i = 0;
+ var l = list.length;
+ for (; i < l; i++) {
+ if (list[i] === elem) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+/**
+ * Return whether an element is contained in a list
+ */
+var contains = function(list, elem) {
+ return indexOf(list, elem) !== -1;
+};
+
+/**
+ * Provide a default value if a setting is undefined
+ */
+var deflt = function(setting, defaultIfUndefined) {
+ return setting === undefined ? defaultIfUndefined : setting;
+};
+
+// hyphenate and escape adapted from Facebook's React under Apache 2 license
+
+var uppercase = /([A-Z])/g;
+var hyphenate = function(str) {
+ return str.replace(uppercase, "-$1").toLowerCase();
+};
+
+var ESCAPE_LOOKUP = {
+ "&": "&amp;",
+ ">": "&gt;",
+ "<": "&lt;",
+ "\"": "&quot;",
+ "'": "&#x27;",
+};
+
+var ESCAPE_REGEX = /[&><"']/g;
+
+function escaper(match) {
+ return ESCAPE_LOOKUP[match];
+}
+
+/**
+ * Escapes text to prevent scripting attacks.
+ *
+ * @param {*} text Text value to escape.
+ * @return {string} An escaped string.
+ */
+function escape(text) {
+ return ("" + text).replace(ESCAPE_REGEX, escaper);
+}
+
+/**
+ * A function to set the text content of a DOM element in all supported
+ * browsers. Note that we don't define this if there is no document.
+ */
+var setTextContent;
+if (typeof document !== "undefined") {
+ var testNode = document.createElement("span");
+ if ("textContent" in testNode) {
+ setTextContent = function(node, text) {
+ node.textContent = text;
+ };
+ } else {
+ setTextContent = function(node, text) {
+ node.innerText = text;
+ };
+ }
+}
+
+/**
+ * A function to clear a node.
+ */
+function clearNode(node) {
+ setTextContent(node, "");
+}
+
+module.exports = {
+ contains: contains,
+ deflt: deflt,
+ escape: escape,
+ hyphenate: hyphenate,
+ indexOf: indexOf,
+ setTextContent: setTextContent,
+ clearNode: clearNode,
+};
+
+},{}]},{},[1])(1)
+}); \ No newline at end of file
diff --git a/vendor/assets/stylesheets/katex.css b/vendor/assets/stylesheets/katex.css
new file mode 100644
index 00000000000..e684d697072
--- /dev/null
+++ b/vendor/assets/stylesheets/katex.css
@@ -0,0 +1,934 @@
+@font-face {
+ font-family: 'KaTeX_AMS';
+ src: url('KaTeX_AMS-Regular.eot');
+ src: url('KaTeX_AMS-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_AMS-Regular.woff2') format('woff2'), url('KaTeX_AMS-Regular.woff') format('woff'), url('KaTeX_AMS-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Caligraphic';
+ src: url('KaTeX_Caligraphic-Bold.eot');
+ src: url('KaTeX_Caligraphic-Bold.eot#iefix') format('embedded-opentype'), url('KaTeX_Caligraphic-Bold.woff2') format('woff2'), url('KaTeX_Caligraphic-Bold.woff') format('woff'), url('KaTeX_Caligraphic-Bold.ttf') format('truetype');
+ font-weight: bold;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Caligraphic';
+ src: url('KaTeX_Caligraphic-Regular.eot');
+ src: url('KaTeX_Caligraphic-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Caligraphic-Regular.woff2') format('woff2'), url('KaTeX_Caligraphic-Regular.woff') format('woff'), url('KaTeX_Caligraphic-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Fraktur';
+ src: url('KaTeX_Fraktur-Bold.eot');
+ src: url('KaTeX_Fraktur-Bold.eot#iefix') format('embedded-opentype'), url('KaTeX_Fraktur-Bold.woff2') format('woff2'), url('KaTeX_Fraktur-Bold.woff') format('woff'), url('KaTeX_Fraktur-Bold.ttf') format('truetype');
+ font-weight: bold;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Fraktur';
+ src: url('KaTeX_Fraktur-Regular.eot');
+ src: url('KaTeX_Fraktur-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Fraktur-Regular.woff2') format('woff2'), url('KaTeX_Fraktur-Regular.woff') format('woff'), url('KaTeX_Fraktur-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Main';
+ src: url('KaTeX_Main-Bold.eot');
+ src: url('KaTeX_Main-Bold.eot#iefix') format('embedded-opentype'), url('KaTeX_Main-Bold.woff2') format('woff2'), url('KaTeX_Main-Bold.woff') format('woff'), url('KaTeX_Main-Bold.ttf') format('truetype');
+ font-weight: bold;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Main';
+ src: url('KaTeX_Main-Italic.eot');
+ src: url('KaTeX_Main-Italic.eot#iefix') format('embedded-opentype'), url('KaTeX_Main-Italic.woff2') format('woff2'), url('KaTeX_Main-Italic.woff') format('woff'), url('KaTeX_Main-Italic.ttf') format('truetype');
+ font-weight: normal;
+ font-style: italic;
+}
+@font-face {
+ font-family: 'KaTeX_Main';
+ src: url('KaTeX_Main-Regular.eot');
+ src: url('KaTeX_Main-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Main-Regular.woff2') format('woff2'), url('KaTeX_Main-Regular.woff') format('woff'), url('KaTeX_Main-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Math';
+ src: url('KaTeX_Math-Italic.eot');
+ src: url('KaTeX_Math-Italic.eot#iefix') format('embedded-opentype'), url('KaTeX_Math-Italic.woff2') format('woff2'), url('KaTeX_Math-Italic.woff') format('woff'), url('KaTeX_Math-Italic.ttf') format('truetype');
+ font-weight: normal;
+ font-style: italic;
+}
+@font-face {
+ font-family: 'KaTeX_SansSerif';
+ src: url('KaTeX_SansSerif-Regular.eot');
+ src: url('KaTeX_SansSerif-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_SansSerif-Regular.woff2') format('woff2'), url('KaTeX_SansSerif-Regular.woff') format('woff'), url('KaTeX_SansSerif-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Script';
+ src: url('KaTeX_Script-Regular.eot');
+ src: url('KaTeX_Script-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Script-Regular.woff2') format('woff2'), url('KaTeX_Script-Regular.woff') format('woff'), url('KaTeX_Script-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Size1';
+ src: url('KaTeX_Size1-Regular.eot');
+ src: url('KaTeX_Size1-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Size1-Regular.woff2') format('woff2'), url('KaTeX_Size1-Regular.woff') format('woff'), url('KaTeX_Size1-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Size2';
+ src: url('KaTeX_Size2-Regular.eot');
+ src: url('KaTeX_Size2-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Size2-Regular.woff2') format('woff2'), url('KaTeX_Size2-Regular.woff') format('woff'), url('KaTeX_Size2-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Size3';
+ src: url('KaTeX_Size3-Regular.eot');
+ src: url('KaTeX_Size3-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Size3-Regular.woff2') format('woff2'), url('KaTeX_Size3-Regular.woff') format('woff'), url('KaTeX_Size3-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Size4';
+ src: url('KaTeX_Size4-Regular.eot');
+ src: url('KaTeX_Size4-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Size4-Regular.woff2') format('woff2'), url('KaTeX_Size4-Regular.woff') format('woff'), url('KaTeX_Size4-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'KaTeX_Typewriter';
+ src: url('KaTeX_Typewriter-Regular.eot');
+ src: url('KaTeX_Typewriter-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Typewriter-Regular.woff2') format('woff2'), url('KaTeX_Typewriter-Regular.woff') format('woff'), url('KaTeX_Typewriter-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+.katex-display {
+ display: block;
+ margin: 1em 0;
+ text-align: center;
+}
+.katex-display > .katex {
+ display: inline-block;
+ text-align: initial;
+}
+.katex {
+ font: normal 1.21em KaTeX_Main, Times New Roman, serif;
+ line-height: 1.2;
+ white-space: nowrap;
+ text-indent: 0;
+}
+.katex .katex-html {
+ display: inline-block;
+}
+.katex .katex-mathml {
+ position: absolute;
+ clip: rect(1px, 1px, 1px, 1px);
+ padding: 0;
+ border: 0;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+}
+.katex .base {
+ display: inline-block;
+}
+.katex .strut {
+ display: inline-block;
+}
+.katex .mathit {
+ font-family: KaTeX_Math;
+ font-style: italic;
+}
+.katex .mathbf {
+ font-family: KaTeX_Main;
+ font-weight: bold;
+}
+.katex .amsrm {
+ font-family: KaTeX_AMS;
+}
+.katex .mathbb {
+ font-family: KaTeX_AMS;
+}
+.katex .mathcal {
+ font-family: KaTeX_Caligraphic;
+}
+.katex .mathfrak {
+ font-family: KaTeX_Fraktur;
+}
+.katex .mathtt {
+ font-family: KaTeX_Typewriter;
+}
+.katex .mathscr {
+ font-family: KaTeX_Script;
+}
+.katex .mathsf {
+ font-family: KaTeX_SansSerif;
+}
+.katex .mainit {
+ font-family: KaTeX_Main;
+ font-style: italic;
+}
+.katex .textstyle > .mord + .mop {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mord + .mbin {
+ margin-left: 0.22222em;
+}
+.katex .textstyle > .mord + .mrel {
+ margin-left: 0.27778em;
+}
+.katex .textstyle > .mord + .minner {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mop + .mord {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mop + .mop {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mop + .mrel {
+ margin-left: 0.27778em;
+}
+.katex .textstyle > .mop + .minner {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mbin + .mord {
+ margin-left: 0.22222em;
+}
+.katex .textstyle > .mbin + .mop {
+ margin-left: 0.22222em;
+}
+.katex .textstyle > .mbin + .mopen {
+ margin-left: 0.22222em;
+}
+.katex .textstyle > .mbin + .minner {
+ margin-left: 0.22222em;
+}
+.katex .textstyle > .mrel + .mord {
+ margin-left: 0.27778em;
+}
+.katex .textstyle > .mrel + .mop {
+ margin-left: 0.27778em;
+}
+.katex .textstyle > .mrel + .mopen {
+ margin-left: 0.27778em;
+}
+.katex .textstyle > .mrel + .minner {
+ margin-left: 0.27778em;
+}
+.katex .textstyle > .mclose + .mop {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mclose + .mbin {
+ margin-left: 0.22222em;
+}
+.katex .textstyle > .mclose + .mrel {
+ margin-left: 0.27778em;
+}
+.katex .textstyle > .mclose + .minner {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mpunct + .mord {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mpunct + .mop {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mpunct + .mrel {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mpunct + .mopen {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mpunct + .mclose {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mpunct + .mpunct {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .mpunct + .minner {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .minner + .mord {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .minner + .mop {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .minner + .mbin {
+ margin-left: 0.22222em;
+}
+.katex .textstyle > .minner + .mrel {
+ margin-left: 0.27778em;
+}
+.katex .textstyle > .minner + .mopen {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .minner + .mpunct {
+ margin-left: 0.16667em;
+}
+.katex .textstyle > .minner + .minner {
+ margin-left: 0.16667em;
+}
+.katex .mord + .mop {
+ margin-left: 0.16667em;
+}
+.katex .mop + .mord {
+ margin-left: 0.16667em;
+}
+.katex .mop + .mop {
+ margin-left: 0.16667em;
+}
+.katex .mclose + .mop {
+ margin-left: 0.16667em;
+}
+.katex .minner + .mop {
+ margin-left: 0.16667em;
+}
+.katex .reset-textstyle.textstyle {
+ font-size: 1em;
+}
+.katex .reset-textstyle.scriptstyle {
+ font-size: 0.7em;
+}
+.katex .reset-textstyle.scriptscriptstyle {
+ font-size: 0.5em;
+}
+.katex .reset-scriptstyle.textstyle {
+ font-size: 1.42857em;
+}
+.katex .reset-scriptstyle.scriptstyle {
+ font-size: 1em;
+}
+.katex .reset-scriptstyle.scriptscriptstyle {
+ font-size: 0.71429em;
+}
+.katex .reset-scriptscriptstyle.textstyle {
+ font-size: 2em;
+}
+.katex .reset-scriptscriptstyle.scriptstyle {
+ font-size: 1.4em;
+}
+.katex .reset-scriptscriptstyle.scriptscriptstyle {
+ font-size: 1em;
+}
+.katex .style-wrap {
+ position: relative;
+}
+.katex .vlist {
+ display: inline-block;
+}
+.katex .vlist > span {
+ display: block;
+ height: 0;
+ position: relative;
+}
+.katex .vlist > span > span {
+ display: inline-block;
+}
+.katex .vlist .baseline-fix {
+ display: inline-table;
+ table-layout: fixed;
+}
+.katex .msupsub {
+ text-align: left;
+}
+.katex .mfrac > span > span {
+ text-align: center;
+}
+.katex .mfrac .frac-line {
+ width: 100%;
+}
+.katex .mfrac .frac-line:before {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ content: "";
+ display: block;
+}
+.katex .mfrac .frac-line:after {
+ border-bottom-style: solid;
+ border-bottom-width: 0.04em;
+ content: "";
+ display: block;
+ margin-top: -1px;
+}
+.katex .mspace {
+ display: inline-block;
+}
+.katex .mspace.negativethinspace {
+ margin-left: -0.16667em;
+}
+.katex .mspace.thinspace {
+ width: 0.16667em;
+}
+.katex .mspace.mediumspace {
+ width: 0.22222em;
+}
+.katex .mspace.thickspace {
+ width: 0.27778em;
+}
+.katex .mspace.enspace {
+ width: 0.5em;
+}
+.katex .mspace.quad {
+ width: 1em;
+}
+.katex .mspace.qquad {
+ width: 2em;
+}
+.katex .llap,
+.katex .rlap {
+ width: 0;
+ position: relative;
+}
+.katex .llap > .inner,
+.katex .rlap > .inner {
+ position: absolute;
+}
+.katex .llap > .fix,
+.katex .rlap > .fix {
+ display: inline-block;
+}
+.katex .llap > .inner {
+ right: 0;
+}
+.katex .rlap > .inner {
+ left: 0;
+}
+.katex .katex-logo .a {
+ font-size: 0.75em;
+ margin-left: -0.32em;
+ position: relative;
+ top: -0.2em;
+}
+.katex .katex-logo .t {
+ margin-left: -0.23em;
+}
+.katex .katex-logo .e {
+ margin-left: -0.1667em;
+ position: relative;
+ top: 0.2155em;
+}
+.katex .katex-logo .x {
+ margin-left: -0.125em;
+}
+.katex .rule {
+ display: inline-block;
+ border: solid 0;
+ position: relative;
+}
+.katex .overline .overline-line,
+.katex .underline .underline-line {
+ width: 100%;
+}
+.katex .overline .overline-line:before,
+.katex .underline .underline-line:before {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ content: "";
+ display: block;
+}
+.katex .overline .overline-line:after,
+.katex .underline .underline-line:after {
+ border-bottom-style: solid;
+ border-bottom-width: 0.04em;
+ content: "";
+ display: block;
+ margin-top: -1px;
+}
+.katex .sqrt > .sqrt-sign {
+ position: relative;
+}
+.katex .sqrt .sqrt-line {
+ width: 100%;
+}
+.katex .sqrt .sqrt-line:before {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ content: "";
+ display: block;
+}
+.katex .sqrt .sqrt-line:after {
+ border-bottom-style: solid;
+ border-bottom-width: 0.04em;
+ content: "";
+ display: block;
+ margin-top: -1px;
+}
+.katex .sqrt > .root {
+ margin-left: 0.27777778em;
+ margin-right: -0.55555556em;
+}
+.katex .sizing,
+.katex .fontsize-ensurer {
+ display: inline-block;
+}
+.katex .sizing.reset-size1.size1,
+.katex .fontsize-ensurer.reset-size1.size1 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size1.size2,
+.katex .fontsize-ensurer.reset-size1.size2 {
+ font-size: 1.4em;
+}
+.katex .sizing.reset-size1.size3,
+.katex .fontsize-ensurer.reset-size1.size3 {
+ font-size: 1.6em;
+}
+.katex .sizing.reset-size1.size4,
+.katex .fontsize-ensurer.reset-size1.size4 {
+ font-size: 1.8em;
+}
+.katex .sizing.reset-size1.size5,
+.katex .fontsize-ensurer.reset-size1.size5 {
+ font-size: 2em;
+}
+.katex .sizing.reset-size1.size6,
+.katex .fontsize-ensurer.reset-size1.size6 {
+ font-size: 2.4em;
+}
+.katex .sizing.reset-size1.size7,
+.katex .fontsize-ensurer.reset-size1.size7 {
+ font-size: 2.88em;
+}
+.katex .sizing.reset-size1.size8,
+.katex .fontsize-ensurer.reset-size1.size8 {
+ font-size: 3.46em;
+}
+.katex .sizing.reset-size1.size9,
+.katex .fontsize-ensurer.reset-size1.size9 {
+ font-size: 4.14em;
+}
+.katex .sizing.reset-size1.size10,
+.katex .fontsize-ensurer.reset-size1.size10 {
+ font-size: 4.98em;
+}
+.katex .sizing.reset-size2.size1,
+.katex .fontsize-ensurer.reset-size2.size1 {
+ font-size: 0.71428571em;
+}
+.katex .sizing.reset-size2.size2,
+.katex .fontsize-ensurer.reset-size2.size2 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size2.size3,
+.katex .fontsize-ensurer.reset-size2.size3 {
+ font-size: 1.14285714em;
+}
+.katex .sizing.reset-size2.size4,
+.katex .fontsize-ensurer.reset-size2.size4 {
+ font-size: 1.28571429em;
+}
+.katex .sizing.reset-size2.size5,
+.katex .fontsize-ensurer.reset-size2.size5 {
+ font-size: 1.42857143em;
+}
+.katex .sizing.reset-size2.size6,
+.katex .fontsize-ensurer.reset-size2.size6 {
+ font-size: 1.71428571em;
+}
+.katex .sizing.reset-size2.size7,
+.katex .fontsize-ensurer.reset-size2.size7 {
+ font-size: 2.05714286em;
+}
+.katex .sizing.reset-size2.size8,
+.katex .fontsize-ensurer.reset-size2.size8 {
+ font-size: 2.47142857em;
+}
+.katex .sizing.reset-size2.size9,
+.katex .fontsize-ensurer.reset-size2.size9 {
+ font-size: 2.95714286em;
+}
+.katex .sizing.reset-size2.size10,
+.katex .fontsize-ensurer.reset-size2.size10 {
+ font-size: 3.55714286em;
+}
+.katex .sizing.reset-size3.size1,
+.katex .fontsize-ensurer.reset-size3.size1 {
+ font-size: 0.625em;
+}
+.katex .sizing.reset-size3.size2,
+.katex .fontsize-ensurer.reset-size3.size2 {
+ font-size: 0.875em;
+}
+.katex .sizing.reset-size3.size3,
+.katex .fontsize-ensurer.reset-size3.size3 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size3.size4,
+.katex .fontsize-ensurer.reset-size3.size4 {
+ font-size: 1.125em;
+}
+.katex .sizing.reset-size3.size5,
+.katex .fontsize-ensurer.reset-size3.size5 {
+ font-size: 1.25em;
+}
+.katex .sizing.reset-size3.size6,
+.katex .fontsize-ensurer.reset-size3.size6 {
+ font-size: 1.5em;
+}
+.katex .sizing.reset-size3.size7,
+.katex .fontsize-ensurer.reset-size3.size7 {
+ font-size: 1.8em;
+}
+.katex .sizing.reset-size3.size8,
+.katex .fontsize-ensurer.reset-size3.size8 {
+ font-size: 2.1625em;
+}
+.katex .sizing.reset-size3.size9,
+.katex .fontsize-ensurer.reset-size3.size9 {
+ font-size: 2.5875em;
+}
+.katex .sizing.reset-size3.size10,
+.katex .fontsize-ensurer.reset-size3.size10 {
+ font-size: 3.1125em;
+}
+.katex .sizing.reset-size4.size1,
+.katex .fontsize-ensurer.reset-size4.size1 {
+ font-size: 0.55555556em;
+}
+.katex .sizing.reset-size4.size2,
+.katex .fontsize-ensurer.reset-size4.size2 {
+ font-size: 0.77777778em;
+}
+.katex .sizing.reset-size4.size3,
+.katex .fontsize-ensurer.reset-size4.size3 {
+ font-size: 0.88888889em;
+}
+.katex .sizing.reset-size4.size4,
+.katex .fontsize-ensurer.reset-size4.size4 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size4.size5,
+.katex .fontsize-ensurer.reset-size4.size5 {
+ font-size: 1.11111111em;
+}
+.katex .sizing.reset-size4.size6,
+.katex .fontsize-ensurer.reset-size4.size6 {
+ font-size: 1.33333333em;
+}
+.katex .sizing.reset-size4.size7,
+.katex .fontsize-ensurer.reset-size4.size7 {
+ font-size: 1.6em;
+}
+.katex .sizing.reset-size4.size8,
+.katex .fontsize-ensurer.reset-size4.size8 {
+ font-size: 1.92222222em;
+}
+.katex .sizing.reset-size4.size9,
+.katex .fontsize-ensurer.reset-size4.size9 {
+ font-size: 2.3em;
+}
+.katex .sizing.reset-size4.size10,
+.katex .fontsize-ensurer.reset-size4.size10 {
+ font-size: 2.76666667em;
+}
+.katex .sizing.reset-size5.size1,
+.katex .fontsize-ensurer.reset-size5.size1 {
+ font-size: 0.5em;
+}
+.katex .sizing.reset-size5.size2,
+.katex .fontsize-ensurer.reset-size5.size2 {
+ font-size: 0.7em;
+}
+.katex .sizing.reset-size5.size3,
+.katex .fontsize-ensurer.reset-size5.size3 {
+ font-size: 0.8em;
+}
+.katex .sizing.reset-size5.size4,
+.katex .fontsize-ensurer.reset-size5.size4 {
+ font-size: 0.9em;
+}
+.katex .sizing.reset-size5.size5,
+.katex .fontsize-ensurer.reset-size5.size5 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size5.size6,
+.katex .fontsize-ensurer.reset-size5.size6 {
+ font-size: 1.2em;
+}
+.katex .sizing.reset-size5.size7,
+.katex .fontsize-ensurer.reset-size5.size7 {
+ font-size: 1.44em;
+}
+.katex .sizing.reset-size5.size8,
+.katex .fontsize-ensurer.reset-size5.size8 {
+ font-size: 1.73em;
+}
+.katex .sizing.reset-size5.size9,
+.katex .fontsize-ensurer.reset-size5.size9 {
+ font-size: 2.07em;
+}
+.katex .sizing.reset-size5.size10,
+.katex .fontsize-ensurer.reset-size5.size10 {
+ font-size: 2.49em;
+}
+.katex .sizing.reset-size6.size1,
+.katex .fontsize-ensurer.reset-size6.size1 {
+ font-size: 0.41666667em;
+}
+.katex .sizing.reset-size6.size2,
+.katex .fontsize-ensurer.reset-size6.size2 {
+ font-size: 0.58333333em;
+}
+.katex .sizing.reset-size6.size3,
+.katex .fontsize-ensurer.reset-size6.size3 {
+ font-size: 0.66666667em;
+}
+.katex .sizing.reset-size6.size4,
+.katex .fontsize-ensurer.reset-size6.size4 {
+ font-size: 0.75em;
+}
+.katex .sizing.reset-size6.size5,
+.katex .fontsize-ensurer.reset-size6.size5 {
+ font-size: 0.83333333em;
+}
+.katex .sizing.reset-size6.size6,
+.katex .fontsize-ensurer.reset-size6.size6 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size6.size7,
+.katex .fontsize-ensurer.reset-size6.size7 {
+ font-size: 1.2em;
+}
+.katex .sizing.reset-size6.size8,
+.katex .fontsize-ensurer.reset-size6.size8 {
+ font-size: 1.44166667em;
+}
+.katex .sizing.reset-size6.size9,
+.katex .fontsize-ensurer.reset-size6.size9 {
+ font-size: 1.725em;
+}
+.katex .sizing.reset-size6.size10,
+.katex .fontsize-ensurer.reset-size6.size10 {
+ font-size: 2.075em;
+}
+.katex .sizing.reset-size7.size1,
+.katex .fontsize-ensurer.reset-size7.size1 {
+ font-size: 0.34722222em;
+}
+.katex .sizing.reset-size7.size2,
+.katex .fontsize-ensurer.reset-size7.size2 {
+ font-size: 0.48611111em;
+}
+.katex .sizing.reset-size7.size3,
+.katex .fontsize-ensurer.reset-size7.size3 {
+ font-size: 0.55555556em;
+}
+.katex .sizing.reset-size7.size4,
+.katex .fontsize-ensurer.reset-size7.size4 {
+ font-size: 0.625em;
+}
+.katex .sizing.reset-size7.size5,
+.katex .fontsize-ensurer.reset-size7.size5 {
+ font-size: 0.69444444em;
+}
+.katex .sizing.reset-size7.size6,
+.katex .fontsize-ensurer.reset-size7.size6 {
+ font-size: 0.83333333em;
+}
+.katex .sizing.reset-size7.size7,
+.katex .fontsize-ensurer.reset-size7.size7 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size7.size8,
+.katex .fontsize-ensurer.reset-size7.size8 {
+ font-size: 1.20138889em;
+}
+.katex .sizing.reset-size7.size9,
+.katex .fontsize-ensurer.reset-size7.size9 {
+ font-size: 1.4375em;
+}
+.katex .sizing.reset-size7.size10,
+.katex .fontsize-ensurer.reset-size7.size10 {
+ font-size: 1.72916667em;
+}
+.katex .sizing.reset-size8.size1,
+.katex .fontsize-ensurer.reset-size8.size1 {
+ font-size: 0.28901734em;
+}
+.katex .sizing.reset-size8.size2,
+.katex .fontsize-ensurer.reset-size8.size2 {
+ font-size: 0.40462428em;
+}
+.katex .sizing.reset-size8.size3,
+.katex .fontsize-ensurer.reset-size8.size3 {
+ font-size: 0.46242775em;
+}
+.katex .sizing.reset-size8.size4,
+.katex .fontsize-ensurer.reset-size8.size4 {
+ font-size: 0.52023121em;
+}
+.katex .sizing.reset-size8.size5,
+.katex .fontsize-ensurer.reset-size8.size5 {
+ font-size: 0.57803468em;
+}
+.katex .sizing.reset-size8.size6,
+.katex .fontsize-ensurer.reset-size8.size6 {
+ font-size: 0.69364162em;
+}
+.katex .sizing.reset-size8.size7,
+.katex .fontsize-ensurer.reset-size8.size7 {
+ font-size: 0.83236994em;
+}
+.katex .sizing.reset-size8.size8,
+.katex .fontsize-ensurer.reset-size8.size8 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size8.size9,
+.katex .fontsize-ensurer.reset-size8.size9 {
+ font-size: 1.19653179em;
+}
+.katex .sizing.reset-size8.size10,
+.katex .fontsize-ensurer.reset-size8.size10 {
+ font-size: 1.43930636em;
+}
+.katex .sizing.reset-size9.size1,
+.katex .fontsize-ensurer.reset-size9.size1 {
+ font-size: 0.24154589em;
+}
+.katex .sizing.reset-size9.size2,
+.katex .fontsize-ensurer.reset-size9.size2 {
+ font-size: 0.33816425em;
+}
+.katex .sizing.reset-size9.size3,
+.katex .fontsize-ensurer.reset-size9.size3 {
+ font-size: 0.38647343em;
+}
+.katex .sizing.reset-size9.size4,
+.katex .fontsize-ensurer.reset-size9.size4 {
+ font-size: 0.43478261em;
+}
+.katex .sizing.reset-size9.size5,
+.katex .fontsize-ensurer.reset-size9.size5 {
+ font-size: 0.48309179em;
+}
+.katex .sizing.reset-size9.size6,
+.katex .fontsize-ensurer.reset-size9.size6 {
+ font-size: 0.57971014em;
+}
+.katex .sizing.reset-size9.size7,
+.katex .fontsize-ensurer.reset-size9.size7 {
+ font-size: 0.69565217em;
+}
+.katex .sizing.reset-size9.size8,
+.katex .fontsize-ensurer.reset-size9.size8 {
+ font-size: 0.83574879em;
+}
+.katex .sizing.reset-size9.size9,
+.katex .fontsize-ensurer.reset-size9.size9 {
+ font-size: 1em;
+}
+.katex .sizing.reset-size9.size10,
+.katex .fontsize-ensurer.reset-size9.size10 {
+ font-size: 1.20289855em;
+}
+.katex .sizing.reset-size10.size1,
+.katex .fontsize-ensurer.reset-size10.size1 {
+ font-size: 0.20080321em;
+}
+.katex .sizing.reset-size10.size2,
+.katex .fontsize-ensurer.reset-size10.size2 {
+ font-size: 0.2811245em;
+}
+.katex .sizing.reset-size10.size3,
+.katex .fontsize-ensurer.reset-size10.size3 {
+ font-size: 0.32128514em;
+}
+.katex .sizing.reset-size10.size4,
+.katex .fontsize-ensurer.reset-size10.size4 {
+ font-size: 0.36144578em;
+}
+.katex .sizing.reset-size10.size5,
+.katex .fontsize-ensurer.reset-size10.size5 {
+ font-size: 0.40160643em;
+}
+.katex .sizing.reset-size10.size6,
+.katex .fontsize-ensurer.reset-size10.size6 {
+ font-size: 0.48192771em;
+}
+.katex .sizing.reset-size10.size7,
+.katex .fontsize-ensurer.reset-size10.size7 {
+ font-size: 0.57831325em;
+}
+.katex .sizing.reset-size10.size8,
+.katex .fontsize-ensurer.reset-size10.size8 {
+ font-size: 0.69477912em;
+}
+.katex .sizing.reset-size10.size9,
+.katex .fontsize-ensurer.reset-size10.size9 {
+ font-size: 0.8313253em;
+}
+.katex .sizing.reset-size10.size10,
+.katex .fontsize-ensurer.reset-size10.size10 {
+ font-size: 1em;
+}
+.katex .delimsizing.size1 {
+ font-family: KaTeX_Size1;
+}
+.katex .delimsizing.size2 {
+ font-family: KaTeX_Size2;
+}
+.katex .delimsizing.size3 {
+ font-family: KaTeX_Size3;
+}
+.katex .delimsizing.size4 {
+ font-family: KaTeX_Size4;
+}
+.katex .delimsizing.mult .delim-size1 > span {
+ font-family: KaTeX_Size1;
+}
+.katex .delimsizing.mult .delim-size4 > span {
+ font-family: KaTeX_Size4;
+}
+.katex .nulldelimiter {
+ display: inline-block;
+ width: 0.12em;
+}
+.katex .op-symbol {
+ position: relative;
+}
+.katex .op-symbol.small-op {
+ font-family: KaTeX_Size1;
+}
+.katex .op-symbol.large-op {
+ font-family: KaTeX_Size2;
+}
+.katex .op-limits > .vlist > span {
+ text-align: center;
+}
+.katex .accent > .vlist > span {
+ text-align: center;
+}
+.katex .accent .accent-body > span {
+ width: 0;
+}
+.katex .accent .accent-body.accent-vec > span {
+ position: relative;
+ left: 0.326em;
+}
+.katex .mtable .vertical-separator {
+ display: inline-block;
+ margin: 0 -0.025em;
+ border-right: 0.05em solid black;
+}
+.katex .mtable .arraycolsep {
+ display: inline-block;
+}
+.katex .mtable .col-align-c > .vlist {
+ text-align: center;
+}
+.katex .mtable .col-align-l > .vlist {
+ text-align: left;
+}
+.katex .mtable .col-align-r > .vlist {
+ text-align: right;
+}