summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZeger-Jan van de Weg <zegerjan@gitlab.com>2016-04-05 14:35:01 +0200
committerZeger-Jan van de Weg <zegerjan@gitlab.com>2016-04-05 14:35:01 +0200
commit06e473e2d9cc3ba156bf9960e610c0f4729e0f88 (patch)
tree3d5a5540fd24f72934afb136c01c9b5388d68f72
parent9e5a11f4b679f4a11aab31ca82c347ba6b025b68 (diff)
parent97747233a99376fdf42869e9433ce9a638b2909d (diff)
downloadgitlab-ce-06e473e2d9cc3ba156bf9960e610c0f4729e0f88.tar.gz
Merge branch 'master' into assign-to-issuable-opener
-rw-r--r--.scss-lint.yml106
-rw-r--r--CHANGELOG52
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock8
-rw-r--r--app/assets/javascripts/awards_handler.coffee4
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee8
-rw-r--r--app/assets/javascripts/gl_dropdown.js.coffee132
-rw-r--r--app/assets/javascripts/issuable_context.js.coffee2
-rw-r--r--app/assets/javascripts/issues.js.coffee10
-rw-r--r--app/assets/javascripts/labels_select.js.coffee27
-rw-r--r--app/assets/javascripts/lib/notify.js.coffee30
-rw-r--r--app/assets/javascripts/merge_request_widget.js.coffee72
-rw-r--r--app/assets/javascripts/milestone_select.js.coffee73
-rw-r--r--app/assets/javascripts/right_sidebar.js.coffee55
-rw-r--r--app/assets/javascripts/search_autocomplete.js.coffee279
-rw-r--r--app/assets/javascripts/sidebar.js.coffee1
-rw-r--r--app/assets/javascripts/todos.js.coffee5
-rw-r--r--app/assets/javascripts/users_select.js.coffee26
-rw-r--r--app/assets/stylesheets/behaviors.scss4
-rw-r--r--app/assets/stylesheets/framework/calendar.scss6
-rw-r--r--app/assets/stylesheets/framework/common.scss4
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss60
-rw-r--r--app/assets/stylesheets/framework/filters.scss2
-rw-r--r--app/assets/stylesheets/framework/forms.scss36
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss6
-rw-r--r--app/assets/stylesheets/framework/header.scss32
-rw-r--r--app/assets/stylesheets/framework/mobile.scss4
-rw-r--r--app/assets/stylesheets/framework/nav.scss14
-rw-r--r--app/assets/stylesheets/framework/selects.scss2
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss131
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap_variables.scss4
-rw-r--r--app/assets/stylesheets/framework/typography.scss6
-rw-r--r--app/assets/stylesheets/framework/variables.scss30
-rw-r--r--app/assets/stylesheets/pages/awards.scss2
-rw-r--r--app/assets/stylesheets/pages/ci_projects.scss2
-rw-r--r--app/assets/stylesheets/pages/commit.scss8
-rw-r--r--app/assets/stylesheets/pages/commits.scss2
-rw-r--r--app/assets/stylesheets/pages/editor.scss2
-rw-r--r--app/assets/stylesheets/pages/events.scss4
-rw-r--r--app/assets/stylesheets/pages/lint.scss4
-rw-r--r--app/assets/stylesheets/pages/login.scss2
-rw-r--r--app/assets/stylesheets/pages/notes.scss135
-rw-r--r--app/assets/stylesheets/pages/search.scss142
-rw-r--r--app/assets/stylesheets/pages/todos.scss8
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/controllers/projects/badges_controller.rb5
-rw-r--r--app/controllers/projects/merge_requests_controller.rb22
-rw-r--r--app/controllers/projects/milestones_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb15
-rw-r--r--app/finders/issuable_finder.rb4
-rw-r--r--app/helpers/application_settings_helper.rb4
-rw-r--r--app/helpers/events_helper.rb8
-rw-r--r--app/helpers/issuables_helper.rb8
-rw-r--r--app/helpers/notes_helper.rb6
-rw-r--r--app/helpers/search_helper.rb57
-rw-r--r--app/mailers/notify.rb4
-rw-r--r--app/models/application_setting.rb2
-rw-r--r--app/models/commit.rb4
-rw-r--r--app/models/commit_range.rb4
-rw-r--r--app/models/concerns/issuable.rb3
-rw-r--r--app/models/external_issue.rb2
-rw-r--r--app/models/global_milestone.rb1
-rw-r--r--app/models/issue.rb4
-rw-r--r--app/models/label.rb2
-rw-r--r--app/models/merge_request.rb15
-rw-r--r--app/models/milestone.rb4
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/project.rb16
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/project_services/slack_service/issue_message.rb2
-rw-r--r--app/models/repository.rb25
-rw-r--r--app/models/snippet.rb4
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/issues/move_service.rb28
-rw-r--r--app/services/projects/import_service.rb2
-rw-r--r--app/services/projects/unlink_fork_service.rb19
-rw-r--r--app/services/system_hooks_service.rb22
-rw-r--r--app/services/system_note_service.rb2
-rw-r--r--app/services/todo_service.rb26
-rw-r--r--app/uploaders/file_uploader.rb17
-rw-r--r--app/views/admin/application_settings/_form.html.haml7
-rw-r--r--app/views/admin/builds/_build.html.haml2
-rw-r--r--app/views/admin/dashboard/index.html.haml2
-rw-r--r--app/views/admin/deploy_keys/index.html.haml2
-rw-r--r--app/views/admin/groups/_group.html.haml28
-rw-r--r--app/views/admin/groups/index.html.haml45
-rw-r--r--app/views/admin/labels/index.html.haml12
-rw-r--r--app/views/admin/runners/index.html.haml2
-rw-r--r--app/views/dashboard/todos/_todo.html.haml7
-rw-r--r--app/views/events/_event.html.haml2
-rw-r--r--app/views/events/event/_created_project.html.haml18
-rw-r--r--app/views/layouts/_collapse_button.html.haml4
-rw-r--r--app/views/layouts/_page.html.haml18
-rw-r--r--app/views/layouts/_search.html.haml40
-rw-r--r--app/views/layouts/nav/_admin.html.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml5
-rw-r--r--app/views/layouts/nav/_group.html.haml10
-rw-r--r--app/views/layouts/nav/_profile.html.haml8
-rw-r--r--app/views/layouts/nav/_project.html.haml17
-rw-r--r--app/views/profiles/notifications/show.html.haml33
-rw-r--r--app/views/profiles/two_factor_auths/new.html.haml2
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml17
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/diffs/_image.html.haml7
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml24
-rw-r--r--app/views/projects/merge_requests/widget/_show.html.haml17
-rw-r--r--app/views/projects/notes/_note.html.haml29
-rw-r--r--app/views/projects/notes/discussions/_active.html.haml16
-rw-r--r--app/views/projects/notes/discussions/_commit.html.haml16
-rw-r--r--app/views/projects/notes/discussions/_outdated.html.haml15
-rw-r--r--app/views/search/results/_note.html.haml18
-rw-r--r--app/views/shared/issuable/_filter.html.haml4
-rw-r--r--app/views/shared/issuable/_label_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/_milestone_dropdown.html.haml6
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml1
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--app/views/votes/_votes_block.html.haml2
-rw-r--r--app/workers/project_cache_worker.rb3
-rw-r--r--app/workers/project_destroy_worker.rb2
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/mail_room.yml2
-rw-r--r--db/migrate/20160324020319_remove_todos_for_deleted_issues.rb17
-rw-r--r--db/migrate/20160329144452_add_index_on_pending_delete_projects.rb6
-rw-r--r--db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb17
-rw-r--r--db/migrate/20160331223143_remove_twitter_sharing_enabled_from_application_settings.rb5
-rw-r--r--db/schema.rb5
-rw-r--r--doc/api/labels.md71
-rw-r--r--doc/api/projects.md166
-rw-r--r--doc/api/settings.md3
-rw-r--r--doc/incoming_email/README.md108
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/update/8.6-to-8.7.md146
-rw-r--r--features/dashboard/dashboard.feature8
-rw-r--r--features/dashboard/todos.feature5
-rw-r--r--features/groups.feature4
-rw-r--r--features/project/project.feature9
-rw-r--r--features/steps/dashboard/dashboard.rb19
-rw-r--r--features/steps/dashboard/issues.rb9
-rw-r--r--features/steps/dashboard/todos.rb8
-rw-r--r--features/steps/group/milestones.rb4
-rw-r--r--features/steps/groups.rb4
-rw-r--r--features/steps/project/active_tab.rb4
-rw-r--r--features/steps/project/fork.rb2
-rw-r--r--features/steps/project/merge_requests.rb8
-rw-r--r--features/steps/project/project.rb12
-rw-r--r--features/steps/project/wiki.rb2
-rw-r--r--features/steps/shared/project_tab.rb2
-rw-r--r--fixtures/emojis/digests.json8597
-rw-r--r--lib/api/entities.rb7
-rw-r--r--lib/api/labels.rb24
-rw-r--r--lib/api/projects.rb28
-rw-r--r--lib/award_emoji.rb19
-rw-r--r--lib/banzai/filter.rb2
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb140
-rw-r--r--lib/banzai/filter/autolink_filter.rb1
-rw-r--r--lib/banzai/filter/commit_range_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_reference_filter.rb2
-rw-r--r--lib/banzai/filter/emoji_filter.rb4
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb41
-rw-r--r--lib/banzai/filter/external_link_filter.rb2
-rw-r--r--lib/banzai/filter/gollum_tags_filter.rb3
-rw-r--r--lib/banzai/filter/image_link_filter.rb27
-rw-r--r--lib/banzai/filter/label_reference_filter.rb2
-rw-r--r--lib/banzai/filter/markdown_filter.rb2
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb2
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb4
-rw-r--r--lib/banzai/filter/redactor_filter.rb2
-rw-r--r--lib/banzai/filter/reference_filter.rb151
-rw-r--r--lib/banzai/filter/reference_gatherer_filter.rb2
-rw-r--r--lib/banzai/filter/relative_link_filter.rb1
-rw-r--r--lib/banzai/filter/sanitization_filter.rb3
-rw-r--r--lib/banzai/filter/snippet_reference_filter.rb2
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb1
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb2
-rw-r--r--lib/banzai/filter/upload_link_filter.rb1
-rw-r--r--lib/banzai/filter/user_reference_filter.rb27
-rw-r--r--lib/banzai/filter/yaml_front_matter_filter.rb3
-rw-r--r--lib/banzai/pipeline/base_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb1
-rw-r--r--lib/banzai/pipeline/wiki_pipeline.rb2
-rw-r--r--lib/gitlab/badge/build.rb24
-rw-r--r--lib/gitlab/current_settings.rb1
-rw-r--r--lib/gitlab/email/message/repository_push.rb2
-rw-r--r--lib/gitlab/email/receiver.rb15
-rw-r--r--lib/gitlab/fogbugz_import/client.rb2
-rw-r--r--lib/gitlab/gfm/reference_rewriter.rb9
-rw-r--r--lib/gitlab/gfm/uploads_rewriter.rb51
-rw-r--r--lib/gitlab/incoming_email.rb16
-rw-r--r--lib/gitlab/note_data_builder.rb2
-rw-r--r--lib/gitlab/routing.rb13
-rw-r--r--lib/gitlab/url_builder.rb16
-rw-r--r--lib/tasks/gemojione.rake48
-rw-r--r--lib/tasks/gitlab/check.rake15
-rw-r--r--spec/controllers/admin/users_controller_spec.rb15
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb2
-rw-r--r--spec/factories/file_uploader.rb20
-rw-r--r--spec/factories/forked_project_links.rb5
-rw-r--r--spec/factories_spec.rb16
-rw-r--r--spec/features/atom/users_spec.rb2
-rw-r--r--spec/features/issues/filter_by_milestone_spec.rb40
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb28
-rw-r--r--spec/features/merge_requests/filter_by_milestone_spec.rb38
-rw-r--r--spec/features/search_spec.rb43
-rw-r--r--spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml42
-rw-r--r--spec/fixtures/emails/valid_reply.eml4
-rw-r--r--spec/lib/award_emoji_spec.rb19
-rw-r--r--spec/lib/banzai/filter/image_link_filter_spec.rb24
-rw-r--r--spec/lib/extracts_path_spec.rb2
-rw-r--r--spec/lib/gitlab/badge/build_spec.rb72
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb2
-rw-r--r--spec/lib/gitlab/email/receiver_spec.rb23
-rw-r--r--spec/lib/gitlab/fogbugz_import/client_spec.rb24
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb66
-rw-r--r--spec/lib/gitlab/incoming_email_spec.rb26
-rw-r--r--spec/mailers/notify_spec.rb66
-rw-r--r--spec/mailers/shared/notify.rb76
-rw-r--r--spec/models/application_setting_spec.rb1
-rw-r--r--spec/models/concerns/issuable_spec.rb1
-rw-r--r--spec/models/hooks/system_hook_spec.rb54
-rw-r--r--spec/models/merge_request_spec.rb8
-rw-r--r--spec/models/project_services/slack_service/issue_message_spec.rb10
-rw-r--r--spec/models/project_spec.rb9
-rw-r--r--spec/models/repository_spec.rb41
-rw-r--r--spec/models/user_spec.rb7
-rw-r--r--spec/requests/api/labels_spec.rb29
-rw-r--r--spec/requests/api/merge_requests_spec.rb11
-rw-r--r--spec/requests/api/projects_spec.rb72
-rw-r--r--spec/services/issues/move_service_spec.rb14
-rw-r--r--spec/services/projects/import_service_spec.rb17
-rw-r--r--spec/services/projects/unlink_fork_service_spec.rb32
-rw-r--r--spec/services/todo_service_spec.rb77
-rw-r--r--spec/support/carrierwave.rb7
-rw-r--r--spec/support/filter_spec_helper.rb2
-rw-r--r--spec/support/markdown_feature.rb2
-rw-r--r--spec/support/test_env.rb1
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb4
-rw-r--r--spec/workers/merge_worker_spec.rb2
-rw-r--r--spec/workers/project_cache_worker_spec.rb27
241 files changed, 12171 insertions, 1231 deletions
diff --git a/.scss-lint.yml b/.scss-lint.yml
index 937d3407b60..835a4a88c44 100644
--- a/.scss-lint.yml
+++ b/.scss-lint.yml
@@ -7,21 +7,44 @@ exclude:
- 'app/assets/stylesheets/pages/emojis.scss'
linters:
+ # Reports when you use improper spacing around ! (the "bang") in !default,
+ # !global, !important, and !optional flags.
BangFormat:
enabled: false
+ # Whether or not to prefer `border: 0` over `border: none`.
BorderZero:
enabled: false
+ # Reports when you define a rule set using a selector with chained classes
+ # (a.k.a. adjoining classes).
+ ChainedClasses:
+ enabled: false
+
+ # Prefer hexadecimal color codes over color keywords.
+ # (e.g. `color: green` is a color keyword)
ColorKeyword:
enabled: false
+ # Prefer color literals (keywords or hexadecimal codes) to be used only in
+ # variable declarations. They should be referred to via variables everywhere
+ # else.
ColorVariable:
enabled: false
+ # Which form of comments to prefer in CSS.
Comment:
enabled: false
+
+ # Reports @debug statements (which you probably left behind accidentally).
+ DebugStatement:
+ enabled: false
+ # Rule sets should be ordered as follows:
+ # - @extend declarations
+ # - @include declarations without inner @content
+ # - properties, @include declarations with inner @content
+ # - nested rule sets.
DeclarationOrder:
enabled: false
@@ -32,15 +55,25 @@ linters:
DisableLinterReason:
enabled: true
+ # Reports when you define the same property twice in a single rule set.
DuplicateProperty:
enabled: false
+ # Separate rule, function, and mixin declarations with empty lines.
EmptyLineBetweenBlocks:
enabled: false
+ # Reports when you have an empty rule set.
EmptyRule:
enabled: false
+ # Reports when you have an @extend directive.
+ ExtendDirective:
+ enabled: false
+
+ # Files should always have a final newline. This results in better diffs
+ # when adding lines to the file, since SCM systems such as git won't
+ # think that you touched the last line.
FinalNewline:
enabled: false
@@ -53,12 +86,17 @@ linters:
HexNotation:
enabled: true
+ # Avoid using ID selectors.
IdSelector:
enabled: false
+ # The basenames of @imported SCSS partials should not begin with an
+ # underscore and should not include the filename extension.
ImportPath:
enabled: false
+ # Avoid using !important in properties. It is usually indicative of a
+ # misunderstanding of CSS specificity and can lead to brittle code.
ImportantRule:
enabled: false
@@ -67,33 +105,51 @@ linters:
enabled: true
width: 2
+ # Don't write leading zeros for numeric values with a decimal point.
LeadingZero:
enabled: false
+ # Reports when you define the same selector twice in a single sheet.
MergeableSelector:
enabled: false
+ # Functions, mixins, variables, and placeholders should be declared
+ # with all lowercase letters and hyphens instead of underscores.
NameFormat:
enabled: false
+ # Avoid nesting selectors too deeply.
NestingDepth:
enabled: false
+ # Always use placeholder selectors in @extend.
PlaceholderInExtend:
enabled: false
+ # Sort properties in a strict order.
PropertySortOrder:
enabled: false
+ # Reports when you use an unknown or disabled CSS property
+ # (ignoring vendor-prefixed properties).
PropertySpelling:
enabled: false
+ # Configure which units are allowed for property values.
+ PropertyUnits:
+ enabled: false
+
+ # Pseudo-elements, like ::before, and ::first-letter, should be declared
+ # with two colons. Pseudo-classes, like :hover and :first-child, should
+ # be declared with one colon.
PseudoElement:
enabled: false
+ # Avoid qualifying elements in selectors (also known as "tag-qualifying").
QualifyingElement:
enabled: false
+ # Don't write selectors with a depth of applicability greater than 3.
SelectorDepth:
enabled: false
@@ -113,9 +169,12 @@ linters:
enabled: true
allow_single_line_rule_sets: true
+ # Split selectors onto separate lines after each comma, and have each
+ # individual selector occupy a single line.
SingleLinePerSelector:
enabled: false
+ # Commas in lists should be followed by a space.
SpaceAfterComma:
enabled: false
@@ -128,28 +187,75 @@ linters:
# colon.
SpaceAfterPropertyName:
enabled: true
+
+ # Variables should be formatted with a single space separating the colon
+ # from the variable's value.
+ SpaceAfterVariableColon:
+ enabled: false
+
+ # Variables should be formatted with no space between the name and the
+ # colon.
+ SpaceAfterVariableName:
+ enabled: false
+ # Operators should be formatted with a single space on both sides of an
+ # infix operator.
SpaceAroundOperator:
enabled: false
+ # Opening braces should be preceded by a single space.
SpaceBeforeBrace:
+ enabled: true
+
+ # Parentheses should not be padded with spaces.
+ SpaceBetweenParens:
enabled: false
+ # Enforces that string literals should be written with a consistent form
+ # of quotes (single or double).
StringQuotes:
enabled: false
+ # Property values, @extend, @include, and @import directives, and variable
+ # declarations should always end with a semicolon.
TrailingSemicolon:
enabled: false
+ # Reports lines containing trailing whitespace.
TrailingWhitespace:
enabled: false
+ # Don't write trailing zeros for numeric values with a decimal point.
+ TrailingZero:
+ enabled: false
+
+ # Don't use the `all` keyword to specify transition properties.
+ TransitionAll:
+ enabled: false
+
+ # Numeric values should not contain unnecessary fractional portions.
UnnecessaryMantissa:
enabled: false
+ # Do not use parent selector references (&) when they would otherwise
+ # be unnecessary.
UnnecessaryParentReference:
enabled: false
+
+ # URLs should be valid and not contain protocols or domain names.
+ UrlFormat:
+ enabled: false
+
+ # URLs should always be enclosed within quotes.
+ UrlQuotes:
+ enabled: false
+
+ # Properties, like color and font, are easier to read and maintain
+ # when defined using variables rather than literals.
+ VariableForProperty:
+ enabled: false
+ # Avoid vendor prefixes. Or rather: don't write them yourself.
VendorPrefix:
enabled: false
diff --git a/CHANGELOG b/CHANGELOG
index b0ddf6c3a94..9b69f447852 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,12 +1,18 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.7.0 (unreleased)
+ - All images in discussions and wikis now link to their source files !3464 (Connor Shea).
+ - Improved Markdown rendering performance !3389 (Yorick Peterse)
- Don't attempt to look up an avatar in repo if repo directory does not exist (Stan hu)
- Preserve time notes/comments have been updated at when moving issue
- Make HTTP(s) label consistent on clone bar (Stan Hu)
- - Allow back dating on issues when created through the API
+ - Expose label description in API (Mariusz Jachimowicz)
+ - Allow back dating on issues when created through the API
- Fix avatar stretching by providing a cropping feature
+ - Add endpoints to archive or unarchive a project !3372
- Add links to CI setup documentation from project settings and builds pages
+ - Handle nil descriptions in Slack issue messages (Stan Hu)
+ - Add default scope to projects to exclude projects pending deletion
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
- Allow issues and merge requests to be assigned to the author
@@ -14,6 +20,49 @@ v 8.7.0 (unreleased)
v 8.6.2 (unreleased)
- Comments on confidential issues don't show up in activity feed to non-members
- Fix NoMethodError when visiting CI root path at `/ci`
+ - Gracefully handle notes on deleted commits in merge requests (Stan Hu)
+ - Fix creation of merge requests for orphaned branches (Stan Hu)
+ - Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
+ - Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
+ - Improved UX of the navigation sidebar
+
+v 8.6.4
+ - Don't attempt to fetch any tags from a forked repo (Stan Hu)
+
+v 8.6.3
+ - Mentions on confidential issues doesn't create todos for non-members. !3374
+ - Destroy related todos when an Issue/MR is deleted. !3376
+ - Fix error 500 when target is nil on todo list. !3376
+ - Fix copying uploads when moving issue to another project. !3382
+ - Ensuring Merge Request API returns boolean values for work_in_progress (Abhi Rao). !3432
+ - Fix raw/rendered diff producing different results on merge requests. !3450
+ - Fix commit comment alignment (Stan Hu). !3466
+ - Fix Error 500 when searching for a comment in a project snippet. !3468
+ - Allow temporary email as notification email. !3477
+ - Fix issue with dropdowns not selecting values. !3478
+ - Update gitlab-shell version and doc to 2.6.12. gitlab-org/gitlab-ee!280
+
+v 8.6.2
+ - Fix dropdown alignment. !3298
+ - Fix issuable sidebar overlaps on tablet. !3299
+ - Make dropdowns pixel perfect. !3337
+ - Fix order of steps to prevent PostgreSQL errors when running migration. !3355
+ - Fix bold text in issuable sidebar. !3358
+ - Fix error with anonymous token in applications settings. !3362
+ - Fix the milestone 'upcoming' filter. !3364 + !3368
+ - Fix comments on confidential issues showing up in activity feed to non-members. !3375
+ - Fix `NoMethodError` when visiting CI root path at `/ci`. !3377
+ - Add a tooltip to new branch button in issue page. !3380
+ - Fix an issue hiding the password form when signed-in with a linked account. !3381
+ - Add links to CI setup documentation from project settings and builds pages. !3384
+ - Fix an issue with width of project select dropdown. !3386
+ - Remove redundant `require`s from Banzai files. !3391
+ - Fix error 500 with cancel button on issuable edit form. !3392 + !3417
+ - Fix background when editing a highlighted note. !3423
+ - Remove tabstop from the WIP toggle links. !3426
+ - Ensure private project snippets are not viewable by unauthorized people.
+ - Gracefully handle notes on deleted commits in merge requests (Stan Hu). !3402
+ - Fixed issue with notification settings not saving. !3452
v 8.6.1
- Add option to reload the schema before restoring a database backup. !2807
@@ -86,6 +135,7 @@ v 8.6.0
- Add main language of a project in the list of projects (Tiago Botelho)
- Add #upcoming filter to Milestone filter (Tiago Botelho)
- Add ability to show archived projects on dashboard, explore and group pages
+ - Remove fork link closes all merge requests opened on source project (Florent Baldino)
- Move group activity to separate page
- Create external users which are excluded of internal and private projects unless access was explicitly granted
- Continue parameters are checked to ensure redirection goes to the same instance
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index bc02b8685c1..24ba9a38de6 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.6.11
+2.7.0
diff --git a/Gemfile b/Gemfile
index 006e53e0c10..6327227282a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -214,7 +214,7 @@ gem 'jquery-rails', '~> 4.0.0'
gem 'jquery-scrollto-rails', '~> 1.4.3'
gem 'jquery-ui-rails', '~> 5.0.0'
gem 'raphael-rails', '~> 2.1.2'
-gem 'request_store', '~> 1.2.0'
+gem 'request_store', '~> 1.3.0'
gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1'
gem 'net-ssh', '~> 3.0.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index da27c62acbf..229089f431d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -126,9 +126,9 @@ GEM
coderay (1.1.0)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
- coffee-rails (4.1.0)
+ coffee-rails (4.1.1)
coffee-script (>= 2.2.0)
- railties (>= 4.0.0, < 5.0)
+ railties (>= 4.0.0, < 5.1.x)
coffee-script (2.4.1)
coffee-script-source
execjs
@@ -652,7 +652,7 @@ GEM
redis-store (~> 1.1.0)
redis-store (1.1.7)
redis (>= 2.2)
- request_store (1.2.1)
+ request_store (1.3.0)
rerun (0.11.0)
listen (~> 3.0)
responders (2.1.1)
@@ -1011,7 +1011,7 @@ DEPENDENCIES
redcarpet (~> 3.3.3)
redis-namespace
redis-rails (~> 4.0.0)
- request_store (~> 1.2.0)
+ request_store (~> 1.3.0)
rerun (~> 0.11.0)
responders (~> 2.0)
rouge (~> 1.10.1)
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index 47b080406d4..6a670d5e887 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -1,5 +1,5 @@
class @AwardsHandler
- constructor: (@post_emoji_url, @noteable_type, @noteable_id, @aliases) ->
+ constructor: (@get_emojis_url, @post_emoji_url, @noteable_type, @noteable_id, @aliases) ->
$(".js-add-award").on "click", (event) =>
event.stopPropagation()
event.preventDefault()
@@ -34,7 +34,7 @@ class @AwardsHandler
$("#emoji_search").focus()
else
$('.js-add-award').addClass "is-loading"
- $.get "/emojis", (response) =>
+ $.get @get_emojis_url, (response) =>
$('.js-add-award').removeClass "is-loading"
$(".js-award-holder").append response
setTimeout =>
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index f5e1ca9860d..70fd6f50e9c 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -146,15 +146,11 @@ class Dispatcher
when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
shortcut_handler = new ShortcutsNavigation()
-
# If we haven't installed a custom shortcut handler, install the default one
if not shortcut_handler
new Shortcuts()
initSearch: ->
- opts = $('.search-autocomplete-opts')
- path = opts.data('autocomplete-path')
- project_id = opts.data('autocomplete-project-id')
- project_ref = opts.data('autocomplete-project-ref')
- new SearchAutocomplete(path, project_id, project_ref)
+ # Only when search form is present
+ new SearchAutocomplete() if $('.search').length
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 2b56ab2e6de..4f032a82e58 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -3,6 +3,10 @@ class GitLabDropdownFilter
HAS_VALUE_CLASS = "has-value"
constructor: (@input, @options) ->
+ {
+ @filterInputBlur = true
+ } = @options
+
$inputContainer = @input.parent()
$clearButton = $inputContainer.find('.js-dropdown-input-clear')
@@ -33,7 +37,7 @@ class GitLabDropdownFilter
blur_field = @shouldBlur e.keyCode
search_text = @input.val()
- if blur_field
+ if blur_field and @filterInputBlur
@input.blur()
if @options.remote
@@ -93,27 +97,48 @@ class GitLabDropdown
PAGE_TWO_CLASS = "is-page-two"
ACTIVE_CLASS = "is-active"
+ FILTER_INPUT = '.dropdown-input .dropdown-input-field'
+
constructor: (@el, @options) ->
- self = @
@dropdown = $(@el).parent()
+
+ # Set Defaults
+ {
+ # If no input is passed create a default one
+ @filterInput = @getElement(FILTER_INPUT)
+ @highlight = false
+ @filterInputBlur = true
+ @enterCallback = true
+ } = @options
+
+ self = @
+
+ # If selector was passed
+ if _.isString(@filterInput)
+ @filterInput = @getElement(@filterInput)
+
search_fields = if @options.search then @options.search.fields else [];
if @options.data
- # Remote data
- @remote = new GitLabDropdownRemote @options.data, {
- dataType: @options.dataType,
- beforeSend: @toggleLoading.bind(@)
- success: (data) =>
- @fullData = data
+ # If data is an array
+ if _.isArray @options.data
+ @fullData = @options.data
+ @parseData @options.data
+ else
+ # Remote data
+ @remote = new GitLabDropdownRemote @options.data, {
+ dataType: @options.dataType,
+ beforeSend: @toggleLoading.bind(@)
+ success: (data) =>
+ @fullData = data
- @parseData @fullData
- }
+ @parseData @fullData
+ }
- # Init filiterable
+ # Init filterable
if @options.filterable
- @input = @dropdown.find('.dropdown-input .dropdown-input-field')
-
- @filter = new GitLabDropdownFilter @input,
+ @filter = new GitLabDropdownFilter @filterInput,
+ filterInputBlur: @filterInputBlur
remote: @options.filterRemote
query: @options.data
keys: @options.search.fields
@@ -123,7 +148,8 @@ class GitLabDropdown
@parseData data
@highlightRow 1
enterCallback: =>
- @selectFirstRow()
+ if @enterCallback
+ @selectFirstRow()
# Event listeners
@@ -145,11 +171,14 @@ class GitLabDropdown
selector = ".dropdown-page-one .dropdown-content a"
@dropdown.on "click", selector, (e) ->
- e.preventDefault()
- self.rowClicked $(@)
+ selected = self.rowClicked $(@)
if self.options.clicked
- self.options.clicked.call(@,e)
+ self.options.clicked(selected)
+
+ # Finds an element inside wrapper element
+ getElement: (selector) ->
+ @dropdown.find selector
toggleLoading: ->
$('.dropdown-menu', @dropdown).toggleClass LOADING_CLASS
@@ -194,7 +223,9 @@ class GitLabDropdown
@remote.execute()
if @options.filterable
- @dropdown.find(".dropdown-input-field").focus()
+ @filterInput.focus()
+
+ @dropdown.trigger('shown.gl.dropdown')
hidden: (e) =>
if @options.filterable
@@ -210,6 +241,8 @@ class GitLabDropdown
if @options.hidden
@options.hidden.call(@,e)
+ @dropdown.trigger('hidden.gl.dropdown')
+
# Render the full menu
renderMenu: (html) ->
@@ -234,13 +267,19 @@ class GitLabDropdown
renderItem: (data) ->
html = ""
+ # Divider
return "<li class='divider'></li>" if data is "divider"
+ # Separator is a full-width divider
+ return "<li class='separator'></li>" if data is "separator"
+
+ # Header
+ return "<li class='dropdown-header'>#{data.header}</li>" if data.header?
+
if @options.renderRow
# Call the render function
html = @options.renderRow(data)
else
- selected = if @options.isSelected then @options.isSelected(data) else false
if not selected
value = if @options.id then @options.id(data) else data.id
fieldName = @options.fieldName
@@ -248,13 +287,26 @@ class GitLabDropdown
if field.length
selected = true
- url = if @options.url then @options.url(data) else "#"
- text = if @options.text then @options.text(data) else ""
+ # Set URL
+ if @options.url?
+ url = @options.url(data)
+ else
+ url = if data.url? then data.url else '#'
+
+ # Set Text
+ if @options.text?
+ text = @options.text(data)
+ else
+ text = if data.text? then data.text else ''
+
cssClass = "";
if selected
cssClass = "is-active"
+ if @highlight
+ text = @highlightTextMatches(text, @filterInput.val())
+
html = "<li>"
html += "<a href='#{url}' class='#{cssClass}'>"
html += text
@@ -263,20 +315,26 @@ class GitLabDropdown
return html
+ highlightTextMatches: (text, term) ->
+ occurrences = fuzzaldrinPlus.match(text, term)
+ text.split('').map((character, i) ->
+ if i in occurrences then "<b>#{character}</b>" else character
+ ).join('')
+
noResults: ->
html = "<li>"
- html += "<a href='#' class='dropdown-menu-empty-link is-focused'>"
+ html += "<a class='dropdown-menu-empty-link is-focused'>"
html += "No matching results."
html += "</a>"
html += "</li>"
highlightRow: (index) ->
- if @input.val() isnt ""
+ if @filterInput.val() isnt ""
selector = '.dropdown-content li:first-child a'
if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one .dropdown-content li:first-child a"
- $(selector).addClass 'is-focused'
+ @getElement(selector).addClass 'is-focused'
rowClicked: (el) ->
fieldName = @options.fieldName
@@ -285,17 +343,15 @@ class GitLabDropdown
selectedObject = @renderedData[selectedIndex]
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
+
if el.hasClass(ACTIVE_CLASS)
el.removeClass(ACTIVE_CLASS)
field.remove()
- else
- fieldName = @options.fieldName
- selectedIndex = el.parent().index()
- if @renderedData
- selectedObject = @renderedData[selectedIndex]
- selectedObject.selected = true
- value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
+ # Toggle the dropdown label
+ if @options.toggleLabel
+ $(@el).find(".dropdown-toggle-text").text @options.toggleLabel
+ else
if !value?
field.remove()
@@ -304,7 +360,7 @@ class GitLabDropdown
@dropdown.parent().find("input[name='#{fieldName}']").remove()
# Toggle active class for the tick mark
- el.toggleClass "is-active"
+ el.addClass ACTIVE_CLASS
# Toggle the dropdown label
if @options.toggleLabel
@@ -313,10 +369,14 @@ class GitLabDropdown
if !field.length
# Create hidden input for form
input = "<input type='hidden' name='#{fieldName}' value='#{value}' />"
- if @options.inputId?
+ if @options.inputId?
input = $(input)
.attr('id', @options.inputId)
@dropdown.before input
+ else
+ field.val value
+
+ return selectedObject
selectFirstRow: ->
selector = '.dropdown-content li:first-child a'
@@ -328,4 +388,6 @@ class GitLabDropdown
$.fn.glDropdown = (opts) ->
return @.each ->
- new GitLabDropdown @, opts
+ if (!$.data @, 'glDropdown')
+ $.data(@, 'glDropdown', new GitLabDropdown @, opts)
+
diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee
index 6fc924d3d66..2f19513a831 100644
--- a/app/assets/javascripts/issuable_context.js.coffee
+++ b/app/assets/javascripts/issuable_context.js.coffee
@@ -9,7 +9,7 @@ class @IssuableContext
$(".issuable-sidebar .inline-update").on "change", ".js-assignee", ->
$(this).submit()
- $(document).on "click",".edit-link", (e) ->
+ $(document).off("click", ".edit-link").on "click",".edit-link", (e) ->
$block = $(@).parents('.block')
$selectbox = $block.find('.selectbox')
if $selectbox.is(':visible')
diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee
index 1127b289264..b1479bfb449 100644
--- a/app/assets/javascripts/issues.js.coffee
+++ b/app/assets/javascripts/issues.js.coffee
@@ -1,7 +1,6 @@
@Issues =
init: ->
Issues.initSearch()
- Issues.initSelects()
Issues.initChecks()
$("body").on "ajax:success", ".close_issue, .reopen_issue", ->
@@ -17,18 +16,9 @@
$(this).html totalIssues - 1
reload: ->
- Issues.initSelects()
Issues.initChecks()
$('#filter_issue_search').val($('#issue_search').val())
- initSelects: ->
- $("select#update_state_event").select2(width: 'resolve', dropdownAutoWidth: true)
- $("select#update_assignee_id").select2(width: 'resolve', dropdownAutoWidth: true)
- $("select#update_milestone_id").select2(width: 'resolve', dropdownAutoWidth: true)
- $("select#label_name").select2(width: 'resolve', dropdownAutoWidth: true)
- $("#milestone_id, #assignee_id, #label_name").on "change", ->
- $(this).closest("form").submit()
-
initChecks: ->
$(".check_all_issues").click ->
$(".selected_issue").prop("checked", @checked)
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index b5c7af9a8ad..d1fe116397a 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -16,6 +16,7 @@ class @LabelsSelect
abilityName = $dropdown.data('ability-name')
$selectbox = $dropdown.closest('.selectbox')
$block = $selectbox.closest('.block')
+ $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span')
$value = $block.find('.value')
$loading = $block.find('.block-loading').fadeOut()
@@ -142,6 +143,7 @@ class @LabelsSelect
if not selected.length
data[abilityName].label_ids = ['']
$loading.fadeIn()
+ $dropdown.trigger('loading.gl.dropdown')
$.ajax(
type: 'PUT'
url: issueUpdateURL
@@ -149,15 +151,20 @@ class @LabelsSelect
data: data
).done (data) ->
$loading.fadeOut()
+ $dropdown.trigger('loaded.gl.dropdown')
$selectbox.hide()
data.issueURLSplit = issueURLSplit
- if not data.labels.length
- template = labelNoneHTMLTemplate()
- else
+ labelCount = 0
+ if data.labels.length
template = labelHTMLTemplate(data)
- href = $value
- .show()
- .html(template)
+ labelCount = data.labels.length
+ else
+ template = labelNoneHTMLTemplate()
+ $value
+ .removeAttr('style')
+ .html(template)
+ $sidebarCollapsedValue.text(labelCount)
+
$value
.find('a')
.each((i) ->
@@ -226,18 +233,20 @@ class @LabelsSelect
hidden: ->
$selectbox.hide()
- $value.show()
+ # display:block overrides the hide-collapse rule
+ $value.removeAttr('style')
if $dropdown.hasClass 'js-multiselect'
saveLabelData()
multiSelect: $dropdown.hasClass 'js-multiselect'
-
- clicked: ->
+ clicked: (label) ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index'
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
+ selectedLabel = label.title
+
Issues.filterResults $dropdown.closest('form')
else if $dropdown.hasClass 'js-filter-submit'
$dropdown.closest('form').submit()
diff --git a/app/assets/javascripts/lib/notify.js.coffee b/app/assets/javascripts/lib/notify.js.coffee
new file mode 100644
index 00000000000..3f9ca39912c
--- /dev/null
+++ b/app/assets/javascripts/lib/notify.js.coffee
@@ -0,0 +1,30 @@
+((w) ->
+ notificationGranted = (message, opts, onclick) ->
+ notification = new Notification(message, opts)
+
+ if onclick
+ notification.onclick = onclick
+
+ notifyPermissions = ->
+ if 'Notification' of window
+ Notification.requestPermission()
+
+ notifyMe = (message, body, icon, onclick) ->
+ opts =
+ body: body
+ icon: icon
+ # Let's check if the browser supports notifications
+ if !('Notification' of window)
+ # do nothing
+ else if Notification.permission == 'granted'
+ # If it's okay let's create a notification
+ notificationGranted message, opts, onclick
+ else if Notification.permission != 'denied'
+ Notification.requestPermission (permission) ->
+ # If the user accepts, let's create a notification
+ if permission == 'granted'
+ notificationGranted message, opts, onclick
+
+ w.notify = notifyMe
+ w.notifyPermissions = notifyPermissions
+) window
diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee
index 738ffc8343b..7102a0673e9 100644
--- a/app/assets/javascripts/merge_request_widget.js.coffee
+++ b/app/assets/javascripts/merge_request_widget.js.coffee
@@ -2,13 +2,18 @@ class @MergeRequestWidget
# Initialize MergeRequestWidget behavior
#
# check_enable - Boolean, whether to check automerge status
- # url_to_automerge_check - String, URL to use to check automerge status
- # current_status - String, current automerge status
- # ci_enable - Boolean, whether a CI service is enabled
- # url_to_ci_check - String, URL to use to check CI status
+ # merge_check_url - String, URL to use to check automerge status
+ # ci_status_url - String, URL to use to check CI status
#
+
constructor: (@opts) ->
- modal = $('#modal_merge_info').modal(show: false)
+ $('#modal_merge_info').modal(show: false)
+ @firstCICheck = true
+ @readyForCICheck = true
+ clearInterval @fetchBuildStatusInterval
+
+ @pollCIStatus()
+ notifyPermissions()
mergeInProgress: (deleteSourceBranch = false)->
$.ajax
@@ -27,18 +32,57 @@ class @MergeRequestWidget
dataType: 'json'
getMergeStatus: ->
- $.get @opts.url_to_automerge_check, (data) ->
+ $.get @opts.merge_check_url, (data) ->
$('.mr-state-widget').replaceWith(data)
- getCiStatus: ->
- if @opts.ci_enable
- $.get @opts.url_to_ci_check, (data) =>
- this.showCiState data.status
+ ciLabelForStatus: (status) ->
+ if status == 'success'
+ 'passed'
+ else
+ status
+
+ pollCIStatus: ->
+ @fetchBuildStatusInterval = setInterval ( =>
+ return if not @readyForCICheck
+
+ @getCIStatus(true)
+
+ @readyForCICheck = false
+ ), 5000
+
+ getCIStatus: (showNotification) ->
+ _this = @
+ $('.ci-widget-fetching').show()
+
+ $.getJSON @opts.ci_status_url, (data) =>
+ @readyForCICheck = true
+
+ if @firstCICheck
+ @firstCICheck = false
+ @opts.ci_status = data.status
+
+ if data.status isnt @opts.ci_status
+ @showCIStatus data.status
if data.coverage
- this.showCiCoverage data.coverage
- , 'json'
+ @showCICoverage data.coverage
+
+ if showNotification
+ message = @opts.ci_message.replace('{{status}}', @ciLabelForStatus(data.status))
+ message = message.replace('{{sha}}', data.sha)
+ message = message.replace('{{title}}', data.title)
+
+ notify(
+ "Build #{@ciLabelForStatus(data.status)}",
+ message,
+ @opts.gitlab_icon,
+ ->
+ @close()
+ Turbolinks.visit _this.opts.builds_path
+ )
+
+ @opts.ci_status = data.status
- showCiState: (state) ->
+ showCIStatus: (state) ->
$('.ci_widget').hide()
allowed_states = ["failed", "canceled", "running", "pending", "success", "skipped", "not_found"]
if state in allowed_states
@@ -52,7 +96,7 @@ class @MergeRequestWidget
$('.ci_widget.ci-error').show()
@setMergeButtonClass('btn-danger')
- showCiCoverage: (coverage) ->
+ showCICoverage: (coverage) ->
text = 'Coverage ' + coverage + '%'
$('.ci_widget:visible .ci-coverage').text(text)
diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee
index d1746c38e74..f73127f49f0 100644
--- a/app/assets/javascripts/milestone_select.js.coffee
+++ b/app/assets/javascripts/milestone_select.js.coffee
@@ -11,12 +11,14 @@ class @MilestoneSelect
selectedMilestone = $dropdown.data('selected')
showNo = $dropdown.data('show-no')
showAny = $dropdown.data('show-any')
+ showUpcoming = $dropdown.data('show-upcoming')
useId = $dropdown.data('use-id')
defaultLabel = $dropdown.data('default-label')
issuableId = $dropdown.data('issuable-id')
abilityName = $dropdown.data('ability-name')
$selectbox = $dropdown.closest('.selectbox')
$block = $selectbox.closest('.block')
+ $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon')
$value = $block.find('.value')
$loading = $block.find('.block-loading').fadeOut()
@@ -32,22 +34,32 @@ class @MilestoneSelect
$.ajax(
url: milestonesUrl
).done (data) ->
- if $dropdown.hasClass "js-extra-options"
- if showNo
- data.unshift(
- id: '0'
- title: 'No Milestone'
- )
+ extraOptions = []
+ if showAny
+ extraOptions.push(
+ id: 0
+ name: ''
+ title: 'Any Milestone'
+ )
- if showAny
- data.unshift(
- isAny: true
- title: 'Any Milestone'
- )
+ if showNo
+ extraOptions.push(
+ id: -1
+ name: 'No Milestone'
+ title: 'No Milestone'
+ )
- if data.length > 2
- data.splice 2, 0, 'divider'
- callback(data)
+ if showUpcoming
+ extraOptions.push(
+ id: -2
+ name: '#upcoming'
+ title: 'Upcoming'
+ )
+
+ if extraOptions.length > 2
+ extraOptions.push 'divider'
+
+ callback(extraOptions.concat(data))
filterable: true
search:
fields: ['title']
@@ -62,23 +74,26 @@ class @MilestoneSelect
milestone.title
id: (milestone) ->
if !useId
- if !milestone.isAny?
- milestone.title
- else
- ''
+ milestone.name
else
milestone.id
isSelected: (milestone) ->
- milestone.title is selectedMilestone
+ milestone.name is selectedMilestone
hidden: ->
$selectbox.hide()
- $value.show()
- clicked: (e) ->
+
+ # display:block overrides the hide-collapse rule
+ $value.removeAttr('style')
+ clicked: (selected) ->
if $dropdown.hasClass 'js-filter-bulk-update'
return
-
- if $dropdown.hasClass 'js-filter-submit'
- $dropdown.parents('form').submit()
+
+ if $dropdown.hasClass('js-filter-submit')
+ if selected.name?
+ selectedMilestone = selected.name
+ else
+ selectedMilestone = ''
+ Issues.filterResults $dropdown.closest('form')
else
selected = $selectbox
.find('input[type="hidden"]')
@@ -88,20 +103,22 @@ class @MilestoneSelect
data[abilityName].milestone_id = selected
$loading
.fadeIn()
+ $dropdown.trigger('loading.gl.dropdown')
$.ajax(
type: 'PUT'
url: issueUpdateURL
data: data
).done (data) ->
+ $dropdown.trigger('loaded.gl.dropdown')
$loading.fadeOut()
$selectbox.hide()
- $milestoneLink = $value
- .show()
- .find('a')
+ $value.removeAttr('style')
if data.milestone?
data.milestone.namespace = _this.currentProject.namespace
data.milestone.path = _this.currentProject.path
$value.html(milestoneLinkTemplate(data.milestone))
+ $sidebarCollapsedValue.find('span').text(data.milestone.title)
else
$value.html(milestoneLinkNoneTemplate)
- ) \ No newline at end of file
+ $sidebarCollapsedValue.find('span').text('No')
+ )
diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee
new file mode 100644
index 00000000000..67403554340
--- /dev/null
+++ b/app/assets/javascripts/right_sidebar.js.coffee
@@ -0,0 +1,55 @@
+class @Sidebar
+ constructor: (currentUser) ->
+ @addEventListeners()
+
+ addEventListeners: ->
+ $('aside').on('click', '.sidebar-collapsed-icon', @sidebarCollapseClicked)
+ $('.dropdown').on('hidden.gl.dropdown', @sidebarDropdownHidden)
+ $('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading)
+ $('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded)
+
+ sidebarDropdownLoading: (e) ->
+ $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon')
+ img = $sidebarCollapsedIcon.find('img')
+ i = $sidebarCollapsedIcon.find('i')
+ $loading = $('<i class="fa fa-spinner fa-spin"></i>')
+ if img.length
+ img.before($loading)
+ img.hide()
+ else if i.length
+ i.before($loading)
+ i.hide()
+
+ sidebarDropdownLoaded: (e) ->
+ $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon')
+ img = $sidebarCollapsedIcon.find('img')
+ $sidebarCollapsedIcon.find('i.fa-spin').remove()
+ i = $sidebarCollapsedIcon.find('i')
+ if img.length
+ img.show()
+ else
+ i.show()
+
+
+ sidebarCollapseClicked: (e) ->
+ e.preventDefault()
+ $block = $(@).closest('.block')
+
+ $('aside')
+ .find('.gutter-toggle')
+ .trigger('click')
+ $editLink = $block.find('.edit-link')
+
+ if $editLink.length
+ $editLink.trigger('click')
+ $block.addClass('collapse-after-update')
+ $('.page-with-sidebar').addClass('with-overlay')
+
+ sidebarDropdownHidden: (e) ->
+ $block = $(@).closest('.block')
+ if $block.hasClass('collapse-after-update')
+ $block.removeClass('collapse-after-update')
+ $('.page-with-sidebar').removeClass('with-overlay')
+ $('aside')
+ .find('.gutter-toggle')
+ .trigger('click') \ No newline at end of file
diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee
index c1801365266..030655491bf 100644
--- a/app/assets/javascripts/search_autocomplete.js.coffee
+++ b/app/assets/javascripts/search_autocomplete.js.coffee
@@ -1,11 +1,270 @@
class @SearchAutocomplete
- constructor: (search_autocomplete_path, project_id, project_ref) ->
- project_id = '' unless project_id
- project_ref = '' unless project_ref
- query = "?project_id=" + project_id + "&project_ref=" + project_ref
-
- $("#search").autocomplete
- source: search_autocomplete_path + query
- minLength: 1
- select: (event, ui) ->
- location.href = ui.item.url
+
+ KEYCODE =
+ ESCAPE: 27
+ BACKSPACE: 8
+ ENTER: 13
+
+ constructor: (opts = {}) ->
+ {
+ @wrap = $('.search')
+
+ @optsEl = @wrap.find('.search-autocomplete-opts')
+ @autocompletePath = @optsEl.data('autocomplete-path')
+ @projectId = @optsEl.data('autocomplete-project-id') || ''
+ @projectRef = @optsEl.data('autocomplete-project-ref') || ''
+
+ } = opts
+
+ # Dropdown Element
+ @dropdown = @wrap.find('.dropdown')
+ @dropdownContent = @dropdown.find('.dropdown-content')
+
+ @locationBadgeEl = @getElement('.search-location-badge')
+ @locationText = @getElement('.location-text')
+ @scopeInputEl = @getElement('#scope')
+ @searchInput = @getElement('.search-input')
+ @projectInputEl = @getElement('#search_project_id')
+ @groupInputEl = @getElement('#group_id')
+ @searchCodeInputEl = @getElement('#search_code')
+ @repositoryInputEl = @getElement('#repository_ref')
+ @clearInput = @getElement('.js-clear-input')
+
+ @saveOriginalState()
+
+ # Only when user is logged in
+ @createAutocomplete() if gon.current_user_id
+
+ @searchInput.addClass('disabled')
+
+ @saveTextLength()
+
+ @bindEvents()
+
+ # Finds an element inside wrapper element
+ getElement: (selector) ->
+ @wrap.find(selector)
+
+ saveOriginalState: ->
+ @originalState = @serializeState()
+
+ saveTextLength: ->
+ @lastTextLength = @searchInput.val().length
+
+ createAutocomplete: ->
+ @searchInput.glDropdown
+ filterInputBlur: false
+ filterable: true
+ filterRemote: true
+ highlight: true
+ enterCallback: false
+ filterInput: 'input#search'
+ search:
+ fields: ['text']
+ data: @getData.bind(@)
+
+ getData: (term, callback) ->
+ _this = @
+
+ # Do not trigger request if input is empty
+ return if @searchInput.val() is ''
+
+ # Prevent multiple ajax calls
+ return if @loadingSuggestions
+
+ @loadingSuggestions = true
+
+ jqXHR = $.get(@autocompletePath, {
+ project_id: @projectId
+ project_ref: @projectRef
+ term: term
+ }, (response) ->
+ # Hide dropdown menu if no suggestions returns
+ if !response.length
+ _this.disableAutocomplete()
+ return
+
+ data = []
+
+ # List results
+ firstCategory = true
+ for suggestion in response
+
+ # Add group header before list each group
+ if lastCategory isnt suggestion.category
+ data.push 'separator' if !firstCategory
+
+ firstCategory = false if firstCategory
+
+ data.push
+ header: suggestion.category
+
+ lastCategory = suggestion.category
+
+ data.push
+ text: suggestion.label
+ url: suggestion.url
+
+ # Add option to proceed with the search
+ if data.length
+ data.push('separator')
+ data.push
+ text: "Result name contains \"#{term}\""
+ url: "/search?\
+ search=#{term}\
+ &project_id=#{_this.projectInputEl.val()}\
+ &group_id=#{_this.groupInputEl.val()}"
+
+ callback(data)
+ ).always ->
+ _this.loadingSuggestions = false
+
+ serializeState: ->
+ {
+ # Search Criteria
+ search_project_id: @projectInputEl.val()
+ group_id: @groupInputEl.val()
+ search_code: @searchCodeInputEl.val()
+ repository_ref: @repositoryInputEl.val()
+ scope: @scopeInputEl.val()
+
+ # Location badge
+ _location: @locationText.text()
+ }
+
+ bindEvents: ->
+ @searchInput.on 'keydown', @onSearchInputKeyDown
+ @searchInput.on 'keyup', @onSearchInputKeyUp
+ @searchInput.on 'click', @onSearchInputClick
+ @searchInput.on 'focus', @onSearchInputFocus
+ @searchInput.on 'blur', @onSearchInputBlur
+ @clearInput.on 'click', @onRemoveLocationClick
+
+ enableAutocomplete: ->
+ # No need to enable anything if user is not logged in
+ return if !gon.current_user_id
+
+ _this = @
+ @loadingSuggestions = false
+
+ @dropdown.addClass('open')
+ @searchInput.removeClass('disabled')
+
+ onSearchInputKeyDown: =>
+ # Saves last length of the entered text
+ @saveTextLength()
+
+ onSearchInputKeyUp: (e) =>
+ switch e.keyCode
+ when KEYCODE.BACKSPACE
+ # when trying to remove the location badge
+ if @lastTextLength is 0 and @badgePresent()
+ @removeLocationBadge()
+
+ # When removing the last character and no badge is present
+ if @lastTextLength is 1
+ @disableAutocomplete()
+
+ # When removing any character from existin value
+ if @lastTextLength > 1
+ @enableAutocomplete()
+
+ when KEYCODE.ESCAPE
+ @restoreOriginalState()
+
+ else
+ # Handle the case when deleting the input value other than backspace
+ # e.g. Pressing ctrl + backspace or ctrl + x
+ if @searchInput.val() is ''
+ @disableAutocomplete()
+ else
+ # We should display the menu only when input is not empty
+ @enableAutocomplete()
+
+ # Avoid falsy value to be returned
+ return
+
+ onSearchInputClick: (e) =>
+ # Prevents closing the dropdown menu
+ e.stopImmediatePropagation()
+
+ onSearchInputFocus: =>
+ @wrap.addClass('search-active')
+
+ onRemoveLocationClick: (e) =>
+ e.preventDefault()
+ @removeLocationBadge()
+ @searchInput.val('').focus()
+ @skipBlurEvent = true
+
+ onSearchInputBlur: (e) =>
+ @skipBlurEvent = false
+
+ # We should wait to make sure we are not clearing the input instead
+ setTimeout( =>
+ return if @skipBlurEvent
+
+ @wrap.removeClass('search-active')
+
+ # If input is blank then restore state
+ if @searchInput.val() is ''
+ @restoreOriginalState()
+ , 150)
+
+ addLocationBadge: (item) ->
+ category = if item.category? then "#{item.category}: " else ''
+ value = if item.value? then item.value else ''
+
+ html = "<span class='location-badge'>
+ <i class='location-text'>#{category}#{value}</i>
+ </span>"
+ @locationBadgeEl.html(html)
+ @wrap.addClass('has-location-badge')
+
+ restoreOriginalState: ->
+ inputs = Object.keys @originalState
+
+ for input in inputs
+ @getElement("##{input}").val(@originalState[input])
+
+
+ if @originalState._location is ''
+ @locationBadgeEl.empty()
+ else
+ @addLocationBadge(
+ value: @originalState._location
+ )
+
+ @dropdown.removeClass 'open'
+
+ badgePresent: ->
+ @locationBadgeEl.children().length
+
+ resetSearchState: ->
+ inputs = Object.keys @originalState
+
+ for input in inputs
+
+ # _location isnt a input
+ break if input is '_location'
+
+ @getElement("##{input}").val('')
+
+ removeLocationBadge: ->
+ @locationBadgeEl.empty()
+
+ # Reset state
+ @resetSearchState()
+
+ @wrap.removeClass('has-location-badge')
+
+ disableAutocomplete: ->
+ @searchInput.addClass('disabled')
+ @dropdown.removeClass('open')
+ @restoreMenu()
+
+ restoreMenu: ->
+ html = "<ul>
+ <li><a class='dropdown-menu-empty-link is-focused'>Loading...</a></li>
+ </ul>"
+ @dropdownContent.html(html)
diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee
index 860d4f438d0..e1778511240 100644
--- a/app/assets/javascripts/sidebar.js.coffee
+++ b/app/assets/javascripts/sidebar.js.coffee
@@ -4,7 +4,6 @@ expanded = 'page-sidebar-expanded'
toggleSidebar = ->
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('header').toggleClass("header-collapsed header-expanded")
- $('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
setTimeout ( ->
diff --git a/app/assets/javascripts/todos.js.coffee b/app/assets/javascripts/todos.js.coffee
index b6b4bd90e6a..ec2df6c5b73 100644
--- a/app/assets/javascripts/todos.js.coffee
+++ b/app/assets/javascripts/todos.js.coffee
@@ -6,10 +6,12 @@ class @Todos
clearListeners: ->
$('.done-todo').off('click')
$('.js-todos-mark-all').off('click')
+ $('.todo').off('click')
initBtnListeners: ->
$('.done-todo').on('click', @doneClicked)
$('.js-todos-mark-all').on('click', @allDoneClicked)
+ $('.todo').on('click', @goToTodoUrl)
doneClicked: (e) =>
e.preventDefault()
@@ -54,3 +56,6 @@ class @Todos
updateBadges: (data) ->
$('.todos-pending .badge, .todos-pending-count').text data.count
$('.todos-done .badge').text data.done_count
+
+ goToTodoUrl: ->
+ Turbolinks.visit($(this).data('url'))
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 3ffb18045c1..33d3357a1f8 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -19,6 +19,7 @@ class @UsersSelect
$block = $selectbox.closest('.block')
abilityName = $dropdown.data('ability-name')
$value = $block.find('.value')
+ $collapsedSidebar = $block.find('.sidebar-collapsed-user')
$loading = $block.find('.block-loading').fadeOut()
$block.on('click', '.js-assign-yourself', (e) =>
@@ -32,12 +33,14 @@ class @UsersSelect
data[abilityName].assignee_id = selected
$loading
.fadeIn()
+ $dropdown.trigger('loading.gl.dropdown')
$.ajax(
type: 'PUT'
dataType: 'json'
url: issueURL
data: data
).done (data) ->
+ $dropdown.trigger('loaded.gl.dropdown')
$loading.fadeOut()
$selectbox.hide()
@@ -51,11 +54,22 @@ class @UsersSelect
name: 'Unassigned'
username: ''
avatar: ''
+ $value.html(assigneeTemplate(user))
+ $collapsedSidebar.html(collapsedAssigneeTemplate(user))
- $value.html(noAssigneeTemplate(user))
- $value.find('a').attr('href')
- noAssigneeTemplate = _.template(
+ collapsedAssigneeTemplate = _.template(
+ '<% if( avatar ) { %>
+ <a class="author_link" href="/u/<%= username %>">
+ <img width="24" class="avatar avatar-inline s24" alt="" src="<%= avatar %>">
+ <span class="author">Toni Boehm</span>
+ </a>
+ <% } else { %>
+ <i class="fa fa-user"></i>
+ <% } %>'
+ )
+
+ assigneeTemplate = _.template(
'<% if (username) { %>
<a class="author_link " href="/u/<%= username %>">
<% if( avatar ) { %>
@@ -131,9 +145,10 @@ class @UsersSelect
hidden: (e) ->
$selectbox.hide()
- $value.show()
+ # display:block overrides the hide-collapse rule
+ $value.removeAttr('style')
- clicked: ->
+ clicked: (user) ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index'
@@ -141,6 +156,7 @@ class @UsersSelect
return
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
+ selectedId = user.id
Issues.filterResults $dropdown.closest('form')
else if $dropdown.hasClass 'js-filter-submit'
$dropdown.closest('form').submit()
diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss
index 469f4f296ae..542a53f0377 100644
--- a/app/assets/stylesheets/behaviors.scss
+++ b/app/assets/stylesheets/behaviors.scss
@@ -13,10 +13,10 @@
// Toggle between two states.
.js-toggler-container {
- .turn-on { display: block; }
+ .turn-on { display: block; }
.turn-off { display: none; }
&.on {
- .turn-on { display: none; }
+ .turn-on { display: none; }
.turn-off { display: block; }
}
}
diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index e3192823a1a..0b3af592d4a 100644
--- a/app/assets/stylesheets/framework/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
@@ -1,3 +1,9 @@
+.calender-block {
+ @media (min-width: $screen-sm-min) and (max-width: $screen-lg-min) {
+ overflow-x: scroll;
+ }
+}
+
.user-calendar-activities {
.calendar_onclick_hr {
padding: 0;
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 9b676d759e0..db1a8b1bf78 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -121,7 +121,7 @@ p.time {
text-shadow: none;
}
-.thin_area{
+.thin_area {
height: 150px;
}
@@ -148,7 +148,7 @@ li.note {
}
}
-.wiki_content code, .readme code{
+.wiki_content code, .readme code {
background-color: inherit;
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 6c870ed927e..82dc1acbd01 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -42,7 +42,7 @@
font-size: 15px;
text-align: left;
border: 1px solid $dropdown-toggle-border-color;
- border-radius: 2px;
+ border-radius: $dropdown-border-radius;
outline: 0;
text-overflow: ellipsis;
white-space: nowrap;
@@ -75,12 +75,12 @@
width: 240px;
margin-top: 2px;
margin-bottom: 0;
- padding: 10px;
- font-size: 14px;
+ font-size: 15px;
font-weight: normal;
+ padding: 10px 0;
background-color: $dropdown-bg;
border: 1px solid $dropdown-border-color;
- border-radius: $border-radius-base;
+ border-radius: $dropdown-border-radius;
box-shadow: 0 2px 4px $dropdown-shadow-color;
&.is-loading {
@@ -101,9 +101,17 @@
li {
text-align: left;
list-style: none;
+ padding: 0 10px;
}
.divider {
+ height: 1px;
+ margin: 8px 10px;
+ padding: 0;
+ background-color: $dropdown-divider-color;
+ }
+
+ .separator {
width: 100%;
height: 1px;
margin-top: 8px;
@@ -141,6 +149,17 @@
line-height: 16px;
}
}
+
+ .dropdown-header {
+ color: $dropdown-header-color;
+ font-size: 13px;
+ line-height: 22px;
+ padding: 0 10px 10px;
+ }
+
+ .separator + .dropdown-header {
+ padding-top: 2px;
+ }
}
.dropdown-menu-paging {
@@ -158,6 +177,10 @@
.dropdown-menu-back {
display: block;
}
+
+ .dropdown-content {
+ padding: 0 10px;
+ }
}
}
@@ -193,7 +216,7 @@
}
.dropdown-select {
- width: 300px;
+ width: $dropdown-width;
}
.dropdown-menu-align-right {
@@ -222,20 +245,11 @@
}
}
-.dropdown-header {
- padding-left: 5px;
- padding-right: 5px;
- color: $dropdown-header-color;
- font-size: 13px;
- line-height: 22px;
-}
.dropdown-title {
position: relative;
- margin-bottom: 10px;
- padding-left: 30px;
- padding-right: 30px;
- padding-bottom: 10px;
+ padding: 0 0 15px;
+ margin: 0 10px 10px;
font-weight: 600;
line-height: 1;
text-align: center;
@@ -261,21 +275,26 @@
}
.dropdown-menu-close {
- right: 0;
+ right: 7px;
+ width: 20px;
+ height: 20px;
+ top: -1px;
}
.dropdown-menu-back {
- left: 0;
+ left: 7px;
+ top: 2px;
}
.dropdown-input {
position: relative;
margin-bottom: 10px;
+ padding: 0 10px;
.fa {
position: absolute;
top: 10px;
- right: 10px;
+ right: 20px;
color: #c7c7c7;
font-size: 12px;
pointer-events: none;
@@ -285,6 +304,9 @@
display: none;
cursor: pointer;
pointer-events: all;
+ right: 22px;
+ top: 9px;
+ font-size: 14px;
}
&.has-value {
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index b05c5df1bd8..9209347f9bc 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -3,7 +3,7 @@
vertical-align: top;
}
-@media (min-width: $screen-sm-min) {
+@media (min-width: $screen-sm-min) {
.issues-filters,
.issues_bulk_update {
.dropdown-menu-toggle {
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 4cb4129b71b..54cb5461113 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -6,40 +6,6 @@ input {
border-radius: $border-radius-base;
}
-input[type='search'] {
- background-color: white;
- padding-left: 10px;
-}
-
-input[type='search'].search-input {
- background-repeat: no-repeat;
- background-position: 10px;
- background-size: 16px;
- background-position-x: 30%;
- padding-left: 10px;
- background-color: $gray-light;
-
- &.search-input[value=""] {
- background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC');
- }
-
- &.search-input::-webkit-input-placeholder {
- text-align: center;
- }
-
- &.search-input:-moz-placeholder { /* Firefox 18- */
- text-align: center;
- }
-
- &.search-input::-moz-placeholder { /* Firefox 19+ */
- text-align: center;
- }
-
- &.search-input:-ms-input-placeholder {
- text-align: center;
- }
-}
-
input[type='text'].danger {
background: #f2dede!important;
border-color: #d66;
@@ -125,7 +91,7 @@ label {
}
.form-control::-webkit-input-placeholder {
- color: #7f8fa4;
+ color: $gl-placeholder-color;
}
.input-group {
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index c83cf881596..fa9038ebaca 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -33,10 +33,15 @@
background: $color;
}
+ .complex-sidebar .nav-primary {
+ border-right: 1px solid lighten($color, 3%);
+ }
+
.sidebar-wrapper {
background: $color-darker;
.sidebar-user {
+ border-top: 1px solid lighten($color, 3%);
background: $color-darker;
color: $color-light;
@@ -62,7 +67,6 @@
.count {
color: $color-light;
- background: $color-dark;
}
}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 6a68bb5c115..724980b2208 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -36,7 +36,7 @@ header {
padding: 0;
.nav > li > a {
- color: #7f8fa4;
+ color: $gl-icon-color;
font-size: 18px;
padding: 0;
margin: ($header-height - 28) / 2 0;
@@ -62,7 +62,7 @@ header {
background-color: #eee;
}
&.active {
- color: #7f8fa4;
+ color: $gl-icon-color;
}
}
}
@@ -81,14 +81,14 @@ header {
font-size: 19px;
line-height: $header-height;
font-weight: normal;
- color: #4c4e54;
+ color: $gl-text-color;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: top;
white-space: nowrap;
a {
- color: #4c4e54;
+ color: $gl-text-color;
&:hover {
text-decoration: underline;
}
@@ -117,37 +117,17 @@ header {
}
}
- .search {
- margin-right: 10px;
- margin-left: 10px;
- margin-top: ($header-height - 36) / 2;
-
- form {
- margin: 0;
- padding: 0;
- }
-
- .search-input {
- width: 220px;
-
- &:focus {
- @include box-shadow(none);
- outline: none;
- }
- }
- }
-
.impersonation i {
color: $red-normal;
}
}
@mixin collapsed-header {
- margin-left: $sidebar_collapsed_width;
+ margin-left: 40px;
}
.header-collapsed {
- margin-left: $sidebar_collapsed_width;
+ margin-left: 40px;
@media (min-width: $screen-md-min) {
@include collapsed-header;
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index 5ea4f9a49db..66180f38a4f 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -107,7 +107,7 @@
}
.page-title {
- .note_created_ago, .new-issue-link {
+ .note-created-ago, .new-issue-link {
display: none;
}
}
@@ -116,7 +116,7 @@
display: none;
}
- aside:not(.right-sidebar){
+ aside:not(.right-sidebar) {
display: none;
}
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 95bdd6d1ea3..94f5a12ff6a 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -56,6 +56,17 @@
}
}
+ .nav-search {
+ display: inline-block;
+ width: 50%;
+ padding: 11px 0;
+
+ /* Small devices (phones, tablets, 768px and lower) */
+ @media (max-width: $screen-sm-min) {
+ width: 100%;
+ }
+ }
+
.nav-links {
display: inline-block;
width: 50%;
@@ -100,6 +111,7 @@
> form {
display: inline-block;
+ margin-top: -1px;
}
.icon-label {
@@ -110,7 +122,7 @@
height: 34px;
display: inline-block;
position: relative;
- top: 1px;
+ top: 2px;
margin-right: $gl-padding-top;
/* Medium devices (desktops, 992px and up) */
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index e82d052f45a..b2fab387e17 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -51,7 +51,7 @@
padding: 10px 15px;
}
-.select2-drop{
+.select2-drop {
color: #7f8fa4;
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 9d188317783..1d49249dd80 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -144,7 +144,7 @@
}
a {
- padding: 7px 15px;
+ padding: 7px 12px;
font-size: $gl-font-size;
line-height: 24px;
color: $gray;
@@ -169,10 +169,12 @@
}
.count {
- float: right;
- background: #eee;
- padding: 0 8px;
- @include border-radius(6px);
+ &:before {
+ content: '(';
+ }
+ &:after {
+ content: ')';
+ }
}
&.back-link i {
@@ -191,6 +193,27 @@
}
}
+.expand-nav a {
+ color: $gl-icon-color;
+ width: 60px;
+ position: fixed;
+ top: 0;
+ left: 0;
+ font-size: 20px;
+ background: #fff;
+ height: 59px;
+ text-align: center;
+ line-height: 59px;
+ border-bottom: 1px solid #eee;
+ transition-duration: .3s;
+ outline: none;
+ z-index: 100;
+
+ &:hover {
+ text-decoration: none;
+ }
+}
+
.collapse-nav a {
width: $sidebar_width;
position: fixed;
@@ -210,55 +233,12 @@
}
.page-sidebar-collapsed {
- padding-left: $sidebar_collapsed_width;
-
.sidebar-wrapper {
- width: $sidebar_collapsed_width;
-
- .header-logo {
- width: $sidebar_collapsed_width;
-
- a {
- padding-left: ($sidebar_collapsed_width - 36) / 2;
-
- .gitlab-text-container {
- display: none;
- }
- }
- }
-
- .nav-sidebar {
- width: $sidebar_collapsed_width;
-
- li {
- width: auto;
-
- a {
- span {
- display: none;
- }
- }
- }
- }
-
- .collapse-nav a {
- width: $sidebar_collapsed_width;
- }
-
- .sidebar-user {
- padding-left: ($sidebar_collapsed_width - 36) / 2;
- width: $sidebar_collapsed_width;
-
- .username {
- display: none;
- }
- }
+ display: none;
}
}
.page-sidebar-expanded {
- padding-left: $sidebar_collapsed_width;
-
@media (min-width: $screen-md-min) {
padding-left: $sidebar_width;
}
@@ -288,6 +268,10 @@
@media (min-width: $screen-sm-min) {
padding-right: $sidebar_collapsed_width;
}
+
+ .sidebar-collapsed-icon {
+ cursor: pointer;
+ }
}
.right-sidebar-expanded {
@@ -300,4 +284,53 @@
@media (min-width: $screen-md-min) {
padding-right: $gutter_width;
}
+
+ &.with-overlay {
+ padding-right: $sidebar_collapsed_width;
+ }
+}
+
+.complex-sidebar {
+ display: inline-block;
+
+ .nav-primary {
+ width: 61px;
+ float: left;
+ height: 100vh;
+
+ .nav-sidebar {
+ width: 60px;
+
+ li a {
+ width: 60px;
+
+ span {
+ display: none;
+ }
+ }
+ }
+ }
+
+ .nav-secondary {
+ $nav-secondary-width: 168px;
+
+ float: left;
+ width: $nav-secondary-width;
+
+ .nav-sidebar {
+ width: $nav-secondary-width;
+
+ li {
+ width: $nav-secondary-width;
+
+ a {
+ width: $nav-secondary-width;
+
+ i {
+ display: none;
+ }
+ }
+ }
+ }
+ }
}
diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index f63ac033234..c72af5dad0a 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -56,8 +56,8 @@ $component-active-bg: $brand-info;
//##
$input-color: $text-color;
-$input-border: #e7e9ed;
-$input-border-focus: #7f8fa4;
+$input-border: $border-color;
+$input-border-focus: $focus-border-color;
$legend-color: $text-color;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index b1886fbe67b..c3c7bc9fdbe 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -138,6 +138,12 @@
}
}
+ a.no-attachment-icon {
+ &:before {
+ display: none;
+ }
+ }
+
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
position: relative;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 61e0dd4d672..98fe794d362 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -11,6 +11,7 @@ $gutter_inner_width: 258px;
* UI elements
*/
$border-color: #efeff1;
+$focus-border-color: #3aabf0;
$table-border-color: #eef0f2;
$background-color: #faf9f9;
@@ -26,6 +27,7 @@ $gl-text-orange: #d90;
$gl-link-color: #3084bb;
$gl-dark-link-color: #333;
$gl-placeholder-color: #8f8f8f;
+$gl-icon-color: $gl-placeholder-color;
$gl-gray: $gl-text-color;
$gl-header-color: $gl-title-color;
@@ -66,7 +68,7 @@ $header-height: 58px;
$fixed-layout-width: 1280px;
$gl-avatar-size: 40px;
$error-exclamation-point: #e62958;
-$border-radius-default: 3px;
+$border-radius-default: 2px;
$btn-transparent-color: #8f8f8f;
$ssh-key-icon-color: #8f8f8f;
$ssh-key-icon-size: 18px;
@@ -166,6 +168,8 @@ $regular_font: 'Source Sans Pro', "Helvetica Neue", Helvetica, Arial, sans-serif
/*
* Dropdowns
*/
+$dropdown-border-radius: 2px;
+$dropdown-width: 300px;
$dropdown-bg: #fff;
$dropdown-link-color: #555;
$dropdown-link-hover-bg: $row-hover;
@@ -176,8 +180,8 @@ $dropdown-divider-color: rgba(#000, .1);
$dropdown-header-color: #959494;
$dropdown-title-btn-color: #bfbfbf;
$dropdown-input-color: #555;
-$dropdown-input-focus-border: rgb(58, 171, 240);
-$dropdown-input-focus-shadow: rgba(#000, .2);
+$dropdown-input-focus-border: $focus-border-color;
+$dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4);
$dropdown-loading-bg: rgba(#fff, .6);
$dropdown-toggle-bg: #fff;
@@ -193,3 +197,23 @@ $dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color;
$award-emoji-menu-bg: #fff;
$award-emoji-menu-border: #f1f2f4;
$award-emoji-new-btn-icon-color: #dcdcdc;
+
+/*
+ * Search Box
+ */
+$search-input-border-color: $dropdown-input-focus-border;
+$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
+$search-input-width: $dropdown-width;
+$location-badge-color: #aaa;
+$location-badge-bg: $gray-normal;
+$location-icon-color: #e7e9ed;
+$location-active-color: $gl-text-color;
+$location-active-bg: $search-input-border-color;
+
+/*
+ * Notes
+ */
+$notes-light-color: #8e8e8e;
+$notes-action-color: #c3c3c3;
+$notes-role-color: #8e8e8e;
+$notes-role-border-color: #e4e4e4;
diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss
index 28994e60baa..37bf38fa65d 100644
--- a/app/assets/stylesheets/pages/awards.scss
+++ b/app/assets/stylesheets/pages/awards.scss
@@ -37,7 +37,7 @@
height: 300px;
overflow-y: scroll;
- input.emoji-search{
+ input.emoji-search {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC");
background-repeat: no-repeat;
background-position: right 5px center;
diff --git a/app/assets/stylesheets/pages/ci_projects.scss b/app/assets/stylesheets/pages/ci_projects.scss
index 2a7b5cfc7fd..67a9d7d2cf7 100644
--- a/app/assets/stylesheets/pages/ci_projects.scss
+++ b/app/assets/stylesheets/pages/ci_projects.scss
@@ -42,7 +42,7 @@
}
}
- .loading{
+ .loading {
font-size: 20px;
}
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 971656feb42..082911bd118 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -1,15 +1,15 @@
-.commit-title{
+.commit-title {
display: block;
}
-.commit-author, .commit-committer{
+.commit-author, .commit-committer {
display: block;
color: #999;
font-weight: normal;
font-style: italic;
}
-.commit-author strong, .commit-committer strong{
+.commit-author strong, .commit-committer strong {
font-weight: bold;
font-style: normal;
}
@@ -74,7 +74,7 @@
color: $gl-text-red;
}
}
- .edit-file{
+ .edit-file {
a {
color: $gl-text-color;
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index b6011fe7679..8272615768d 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -1,4 +1,4 @@
-.commits-compare-switch{
+.commits-compare-switch {
@include btn-default;
@include btn-white;
background: image-url("switch_icon.png") no-repeat center center;
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 43be5e38ba8..0f0592a0ab8 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -1,5 +1,5 @@
.file-editor {
- #editor{
+ #editor {
border: none;
@include border-radius(0);
height: 500px;
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 84eefd01cfe..c66efe978cd 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -43,10 +43,6 @@
.md {
color: #7f8fa4;
font-size: $gl-font-size;
-
- iframe.twitter-share-button {
- vertical-align: bottom;
- }
}
pre {
diff --git a/app/assets/stylesheets/pages/lint.scss b/app/assets/stylesheets/pages/lint.scss
index 6d2bd33b28b..6926448519e 100644
--- a/app/assets/stylesheets/pages/lint.scss
+++ b/app/assets/stylesheets/pages/lint.scss
@@ -1,9 +1,9 @@
.ci-body {
- .incorrect-syntax{
+ .incorrect-syntax {
font-size: 19px;
color: red;
}
- .correct-syntax{
+ .correct-syntax {
font-size: 19px;
color: #47a447;
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 777bcbca5c3..403171d4532 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -36,7 +36,7 @@
}
}
- .login-box{
+ .login-box {
background: #fafafa;
border-radius: 10px;
box-shadow: 0 0 2px #ccc;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 4bd2016bdcf..92fcaaeeacf 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -22,7 +22,7 @@ ul.notes {
margin-left: 55px;
}
- .note_created_ago, .note-updated-at {
+ .note-created-ago, .note-updated-at {
white-space: nowrap;
}
@@ -39,53 +39,6 @@ ul.notes {
}
}
- .discussion-header,
- .note-header {
- @extend .cgray;
-
- a:hover {
- text-decoration: none;
- }
-
- .avatar {
- float: left;
- margin-right: 10px;
- }
-
- .discussion-last-update,
- .note-last-update {
- &:before {
- content: "\00b7";
- }
-
- a {
- color: $gl-gray;
-
- &:hover {
- text-decoration: underline;
- }
- }
- }
- .author {
- color: #4c4e54;
- margin-right: 3px;
-
- &:hover {
- color: $gl-link-color;
- }
- }
- .author-username {
- }
-
- .note-role {
- float: right;
- margin-top: 1px;
- border: 1px solid #bbb;
- background-color: transparent;
- color: $gl-gray;
- }
- }
-
.discussion-body {
padding-top: 15px;
}
@@ -198,40 +151,88 @@ ul.notes {
border-width: 1px 0;
padding-top: 0;
vertical-align: top;
- &.parallel{
+ &.parallel {
border-width: 1px;
}
}
}
}
+.discussion-header,
+.note-header {
+ a {
+ color: inherit;
+
+ &:hover {
+ color: $gl-link-color;
+ text-decoration: none;
+ }
+ }
+
+ .author_link {
+ font-weight: 600;
+ }
+}
+
+.note-headline-light,
+.discussion-headline-light {
+ color: $notes-light-color;
+}
+
/**
* Actions for Discussions/Notes
*/
-.discussion,
-.note {
- .discussion-actions,
- .note-actions {
- float: right;
- margin-left: 10px;
+.discussion-actions,
+.note-actions {
+ float: right;
+ margin-left: 10px;
+ color: $notes-action-color;
+}
- a {
- margin-left: 5px;
- color: $gl-gray;
+.note-action-button,
+.discussion-action-button {
+ display: inline-block;
+ margin-left: 10px;
+ line-height: 24px;
- i.fa {
- font-size: 16px;
- line-height: 16px;
- }
+ .fa {
+ position: relative;
+ top: 1px;
+ font-size: 17px;
+ }
- &:hover {
- @extend .cgray;
- &.danger { @extend .cred; }
- }
- }
+ .fa-trash-o {
+ top: 0;
+ font-size: 16px;
}
}
+
+.discussion-toggle-button {
+ line-height: 20px;
+ font-size: 13px;
+
+ .fa {
+ margin-right: 3px;
+ font-size: 10px;
+ line-height: 18px;
+ vertical-align: top;
+ }
+}
+
+.note-role {
+ position: relative;
+ top: -2px;
+ display: inline-block;
+ padding-left: 4px;
+ padding-right: 4px;
+ color: $notes-role-color;
+ font-size: 12px;
+ line-height: 20px;
+ border: 1px solid $notes-role-border-color;
+ border-radius: $border-radius-base;
+}
+
.diff-file .note .note-actions {
right: 0;
top: 0;
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index b6e45024644..3c74d25beb0 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -21,3 +21,145 @@
}
}
+.search {
+ margin-right: 10px;
+ margin-left: 10px;
+ margin-top: ($header-height - 35) / 2;
+
+ form {
+ @extend .form-control;
+ margin: 0;
+ padding: 4px;
+ width: $search-input-width;
+ line-height: 24px;
+ }
+
+ .location-text {
+ font-style: normal;
+ }
+
+ .search-input {
+ border: none;
+ font-size: 14px;
+ outline: none;
+ padding: 0;
+ margin-left: 5px;
+ line-height: 25px;
+ width: 98%;
+ }
+
+ .location-badge {
+ line-height: 25px;
+ padding: 0 5px;
+ border-radius: $border-radius-default;
+ font-size: 14px;
+ font-style: normal;
+ color: $location-badge-color;
+ display: inline-block;
+ background-color: $location-badge-bg;
+ vertical-align: top;
+ }
+
+ .search-input-container {
+ display: -webkit-flex;
+ display: flex;
+ position: relative;
+ }
+
+ .search-location-badge, .search-input-wrap {
+ // Fallback if flexbox is not supported
+ display: inline-block;
+ }
+
+ .search-input-wrap {
+ width: 100%;
+
+ .search-icon, .clear-icon {
+ position: absolute;
+ right: 5px;
+ top: 0;
+ color: $location-icon-color;
+
+ &:before {
+ font-family: FontAwesome;
+ font-weight: normal;
+ font-style: normal;
+ }
+ }
+
+ .search-icon {
+ @extend .fa-search;
+ @include transition(color .15s);
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ }
+
+ .clear-icon {
+ @extend .fa-times;
+ display: none;
+ }
+
+ // Rewrite position. Dropdown menu should be relative to .search-input-container
+ .dropdown {
+ position: static;
+ }
+
+ .dropdown-header {
+ text-transform: uppercase;
+ font-size: 11px;
+ }
+
+ // Custom dropdown positioning
+ .dropdown-menu {
+ top: 30px;
+ left: -5px;
+ padding: 0;
+
+ ul {
+ padding: 10px 0;
+ }
+ }
+
+ .dropdown-content {
+ max-height: 350px;
+ }
+ }
+
+ &.search-active {
+ form {
+ @extend .form-control:focus;
+ border-color: $dropdown-input-focus-border;
+ box-shadow: 0 0 4px $search-input-focus-shadow-color;
+ }
+
+ .location-badge {
+ @include transition(all .15s);
+ background-color: $location-active-bg;
+ color: $white-light;
+ }
+
+ .search-input-wrap {
+ i {
+ color: $location-active-color;
+ }
+ }
+
+ &.has-location-badge {
+ .search-icon {
+ display: none;
+ }
+
+ .clear-icon {
+ cursor: pointer;
+ display: block;
+ }
+ }
+ }
+
+ &.has-location-badge {
+ .search-input-wrap {
+ width: 78%;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index f983e9829e6..e83fa9e3d52 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -6,13 +6,19 @@
.navbar-nav {
li {
.badge.todos-pending-count {
- background-color: #7f8fa4;
+ background-color: $gl-icon-color;
margin-top: -5px;
font-weight: normal;
}
}
}
+.todo {
+ &:hover {
+ cursor: pointer;
+ }
+}
+
.todo-item {
.todo-title {
@include str-truncated(calc(100% - 174px));
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index ed9f6031389..f010436bd36 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -52,7 +52,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:require_two_factor_authentication,
:two_factor_grace_period,
:gravatar_enabled,
- :twitter_sharing_enabled,
:sign_in_text,
:help_page_text,
:home_page_url,
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index 6ff47c4033a..6d4d4360988 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -2,11 +2,12 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :no_cache_headers
def build
+ badge = Gitlab::Badge::Build.new(project, params[:ref])
+
respond_to do |format|
format.html { render_404 }
format.svg do
- image = Ci::ImageForBuildService.new.execute(project, ref: params[:ref])
- send_file(image.path, filename: image.name, disposition: 'inline', type: 'image/svg+xml')
+ send_data(badge.data, type: badge.type, disposition: 'inline')
end
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 6189de09f27..49064f5d505 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -57,8 +57,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.html
format.json { render json: @merge_request }
- format.diff { render text: @merge_request.to_diff(current_user) }
- format.patch { render text: @merge_request.to_patch(current_user) }
+ format.diff { render text: @merge_request.to_diff }
+ format.patch { render text: @merge_request.to_patch }
end
end
@@ -154,7 +154,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.target_project, @merge_request])
end
format.json do
- render json: @merge_request.to_json(include: [:milestone, :labels, :assignee])
+ render json: @merge_request.to_json(include: [:milestone, :labels, assignee: { methods: :avatar_url }])
end
end
else
@@ -224,14 +224,22 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def ci_status
- ci_service = @merge_request.source_project.ci_service
- status = ci_service.commit_status(merge_request.last_commit.sha, merge_request.source_branch)
+ ci_commit = @merge_request.ci_commit
+ if ci_commit
+ status = ci_commit.status
+ coverage = ci_commit.try(:coverage)
+ else
+ ci_service = @merge_request.source_project.ci_service
+ status = ci_service.commit_status(merge_request.last_commit.sha, merge_request.source_branch) if ci_service
- if ci_service.respond_to?(:commit_coverage)
- coverage = ci_service.commit_coverage(merge_request.last_commit.sha, merge_request.source_branch)
+ if ci_service.respond_to?(:commit_coverage)
+ coverage = ci_service.commit_coverage(merge_request.last_commit.sha, merge_request.source_branch)
+ end
end
response = {
+ title: merge_request.title,
+ sha: merge_request.last_commit_short_sha,
status: status,
coverage: coverage
}
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 5b0a63a933c..f7b6d137bde 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -24,7 +24,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestones = @milestones.page(params[:page])
end
format.json do
- render json: @milestones
+ render json: @milestones.to_json(methods: :name)
end
end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 928817ba811..8c3a74c8236 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -71,7 +71,7 @@ class ProjectsController < Projects::ApplicationController
def remove_fork
return access_denied! unless can?(current_user, :remove_fork_project, @project)
- if @project.unlink_fork
+ if ::Projects::UnlinkForkService.new(@project, current_user).execute
flash[:notice] = 'The fork relationship has been removed.'
end
end
@@ -138,7 +138,7 @@ class ProjectsController < Projects::ApplicationController
participants = ::Projects::ParticipantsService.new(@project, current_user).execute(note_type, note_id)
@suggestions = {
- emojis: autocomplete_emojis,
+ emojis: AwardEmoji.urls,
issues: autocomplete.issues,
mergerequests: autocomplete.merge_requests,
members: participants
@@ -235,17 +235,6 @@ class ProjectsController < Projects::ApplicationController
)
end
- def autocomplete_emojis
- Rails.cache.fetch("autocomplete-emoji-#{Gemojione::VERSION}") do
- Emoji.emojis.map do |name, emoji|
- {
- name: name,
- path: view_context.image_url("#{emoji["unicode"]}.png")
- }
- end
- end
- end
-
def repo_exists?
project.repository_exists? && !project.empty_repo?
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 046286dd9e1..f1df6832bf6 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -243,7 +243,7 @@ class IssuableFinder
end
def filter_by_upcoming_milestone?
- params[:milestone_title] == '#upcoming'
+ params[:milestone_title] == Milestone::Upcoming.name
end
def by_milestone(items)
@@ -252,7 +252,7 @@ class IssuableFinder
items = items.where(milestone_id: [-1, nil])
elsif filter_by_upcoming_milestone?
upcoming = Milestone.where(project_id: projects).upcoming
- items = items.joins(:milestone).where(milestones: { title: upcoming.title })
+ items = items.joins(:milestone).where(milestones: { title: upcoming.try(:title) })
else
items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] })
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 23693629a4c..60a0ff32c9c 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -3,10 +3,6 @@ module ApplicationSettingsHelper
current_application_settings.gravatar_enabled?
end
- def twitter_sharing_enabled?
- current_application_settings.twitter_sharing_enabled?
- end
-
def signup_enabled?
current_application_settings.signup_enabled?
end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index d3e5e3aa8b9..592bad8ba24 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -214,4 +214,12 @@ module EventsHelper
end
end
end
+
+ def event_row_class(event)
+ if event.body?
+ "event-block"
+ else
+ "event-inline"
+ end
+ end
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 62050691a39..b14b8218d02 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -47,6 +47,14 @@ module IssuablesHelper
end
end
+ def milestone_dropdown_label(milestone_title, default_label = "Milestone")
+ if milestone_title == Milestone::Upcoming.name
+ milestone_title = Milestone::Upcoming.title
+ end
+
+ h(milestone_title.presence || default_label)
+ end
+
private
def sidebar_gutter_collapsed?
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 53c543c28c5..698f90cb27a 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -5,8 +5,10 @@ module NotesHelper
end
def note_target_fields(note)
- hidden_field_tag(:target_type, note.noteable.class.name.underscore) +
- hidden_field_tag(:target_id, note.noteable.id)
+ if note.noteable
+ hidden_field_tag(:target_type, note.noteable.class.name.underscore) +
+ hidden_field_tag(:target_id, note.noteable.id)
+ end
end
def note_editable?(note)
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 494dad0b41e..8a97a74ad73 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -1,4 +1,5 @@
module SearchHelper
+
def search_autocomplete_opts(term)
return unless current_user
@@ -23,45 +24,44 @@ module SearchHelper
# Autocomplete results for various settings pages
def default_autocomplete
[
- { label: "Profile settings", url: profile_path },
- { label: "SSH Keys", url: profile_keys_path },
- { label: "Dashboard", url: root_path },
- { label: "Admin Section", url: admin_root_path },
+ { category: "Settings", label: "Profile settings", url: profile_path },
+ { category: "Settings", label: "SSH Keys", url: profile_keys_path },
+ { category: "Settings", label: "Dashboard", url: root_path },
+ { category: "Settings", label: "Admin Section", url: admin_root_path },
]
end
# Autocomplete results for internal help pages
def help_autocomplete
[
- { label: "help: API Help", url: help_page_path("api", "README") },
- { label: "help: Markdown Help", url: help_page_path("markdown", "markdown") },
- { label: "help: Permissions Help", url: help_page_path("permissions", "permissions") },
- { label: "help: Public Access Help", url: help_page_path("public_access", "public_access") },
- { label: "help: Rake Tasks Help", url: help_page_path("raketasks", "README") },
- { label: "help: SSH Keys Help", url: help_page_path("ssh", "README") },
- { label: "help: System Hooks Help", url: help_page_path("system_hooks", "system_hooks") },
- { label: "help: Webhooks Help", url: help_page_path("web_hooks", "web_hooks") },
- { label: "help: Workflow Help", url: help_page_path("workflow", "README") },
+ { category: "Help", label: "API Help", url: help_page_path("api", "README") },
+ { category: "Help", label: "Markdown Help", url: help_page_path("markdown", "markdown") },
+ { category: "Help", label: "Permissions Help", url: help_page_path("permissions", "permissions") },
+ { category: "Help", label: "Public Access Help", url: help_page_path("public_access", "public_access") },
+ { category: "Help", label: "Rake Tasks Help", url: help_page_path("raketasks", "README") },
+ { category: "Help", label: "SSH Keys Help", url: help_page_path("ssh", "README") },
+ { category: "Help", label: "System Hooks Help", url: help_page_path("system_hooks", "system_hooks") },
+ { category: "Help", label: "Webhooks Help", url: help_page_path("web_hooks", "web_hooks") },
+ { category: "Help", label: "Workflow Help", url: help_page_path("workflow", "README") },
]
end
# Autocomplete results for the current project, if it's defined
def project_autocomplete
if @project && @project.repository.exists? && @project.repository.root_ref
- prefix = search_result_sanitize(@project.name_with_namespace)
ref = @ref || @project.repository.root_ref
[
- { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
- { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) },
- { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) },
- { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) },
- { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) },
- { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
- { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
- { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
- { label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) },
- { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
+ { category: "Current Project", label: "Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
+ { category: "Current Project", label: "Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) },
+ { category: "Current Project", label: "Network", url: namespace_project_network_path(@project.namespace, @project, ref) },
+ { category: "Current Project", label: "Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) },
+ { category: "Current Project", label: "Issues", url: namespace_project_issues_path(@project.namespace, @project) },
+ { category: "Current Project", label: "Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
+ { category: "Current Project", label: "Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
+ { category: "Current Project", label: "Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
+ { category: "Current Project", label: "Members", url: namespace_project_project_members_path(@project.namespace, @project) },
+ { category: "Current Project", label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
]
else
[]
@@ -72,7 +72,9 @@ module SearchHelper
def groups_autocomplete(term, limit = 5)
current_user.authorized_groups.search(term).limit(limit).map do |group|
{
- label: "group: #{search_result_sanitize(group.name)}",
+ category: "Groups",
+ id: group.id,
+ label: "#{search_result_sanitize(group.name)}",
url: group_path(group)
}
end
@@ -83,7 +85,10 @@ module SearchHelper
current_user.authorized_projects.search_by_title(term).
sorted_by_stars.non_archived.limit(limit).map do |p|
{
- label: "project: #{search_result_sanitize(p.name_with_namespace)}",
+ category: "Projects",
+ id: p.id,
+ value: "#{search_result_sanitize(p.name)}",
+ label: "#{search_result_sanitize(p.name_with_namespace)}",
url: namespace_project_path(p.namespace, p)
}
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 8cbc9eefc7b..826e5f96fa1 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -110,6 +110,10 @@ class Notify < BaseMailer
headers['Reply-To'] = address
+ fallback_reply_message_id = "<reply-#{reply_key}@#{Gitlab.config.gitlab.host}>".freeze
+ headers['References'] ||= ''
+ headers['References'] << ' ' << fallback_reply_message_id
+
@reply_by_email = true
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index c4879598c4e..052cd874733 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -12,7 +12,6 @@
# updated_at :datetime
# home_page_url :string(255)
# default_branch_protection :integer default(2)
-# twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text
# version_check_enabled :boolean default(TRUE)
# max_attachment_size :integer default(10), not null
@@ -140,7 +139,6 @@ class ApplicationSetting < ActiveRecord::Base
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
- twitter_sharing_enabled: Settings.gitlab['twitter_sharing_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
diff --git a/app/models/commit.rb b/app/models/commit.rb
index d0dbe009d0d..d09876a07d9 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -74,14 +74,14 @@ class Commit
#
# This pattern supports cross-project references.
def self.reference_pattern
- %r{
+ @reference_pattern ||= %r{
(?:#{Project.reference_pattern}#{reference_prefix})?
(?<commit>\h{7,40})
}x
end
def self.link_reference_pattern
- super("commit", /(?<commit>\h{7,40})/)
+ @link_reference_pattern ||= super("commit", /(?<commit>\h{7,40})/)
end
def to_reference(from_project = nil)
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
index 289dbc57287..51673897d98 100644
--- a/app/models/commit_range.rb
+++ b/app/models/commit_range.rb
@@ -43,14 +43,14 @@ class CommitRange
#
# This pattern supports cross-project references.
def self.reference_pattern
- %r{
+ @reference_pattern ||= %r{
(?:#{Project.reference_pattern}#{reference_prefix})?
(?<commit_range>#{STRICT_PATTERN})
}x
end
def self.link_reference_pattern
- super("compare", /(?<commit_range>#{PATTERN})/)
+ @link_reference_pattern ||= super("compare", /(?<commit_range>#{PATTERN})/)
end
# Initialize a CommitRange
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index cf5b2c71675..afa2ca039ae 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -19,6 +19,7 @@ module Issuable
has_many :notes, as: :noteable, dependent: :destroy
has_many :label_links, as: :target, dependent: :destroy
has_many :labels, through: :label_links
+ has_many :todos, as: :target, dependent: :destroy
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
@@ -41,7 +42,7 @@ module Issuable
scope :join_project, -> { joins(:project) }
scope :references_project, -> { references(:project) }
- scope :non_archived, -> { join_project.merge(Project.non_archived.only(:where)) }
+ scope :non_archived, -> { join_project.where(projects: { archived: false }) }
delegate :name,
:email,
diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb
index 2ca79df0a29..b8585d4e577 100644
--- a/app/models/external_issue.rb
+++ b/app/models/external_issue.rb
@@ -31,7 +31,7 @@ class ExternalIssue
# Pattern used to extract `JIRA-123` issue references from text
def self.reference_pattern
- %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)}
+ @reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)}
end
def to_reference(_from_project = nil)
diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb
index 97bd79af083..da7c265a371 100644
--- a/app/models/global_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -14,6 +14,7 @@ class GlobalMilestone
def initialize(title, milestones)
@title = title
+ @name = title
@milestones = milestones
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index ed960cb39f4..e064b0f8b95 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -73,14 +73,14 @@ class Issue < ActiveRecord::Base
#
# This pattern supports cross-project references.
def self.reference_pattern
- %r{
+ @reference_pattern ||= %r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<issue>\d+)
}x
end
def self.link_reference_pattern
- super("issues", /(?<issue>\d+)/)
+ @link_reference_pattern ||= super("issues", /(?<issue>\d+)/)
end
def to_reference(from_project = nil)
diff --git a/app/models/label.rb b/app/models/label.rb
index 500d5a35521..55c01cae762 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -56,7 +56,7 @@ class Label < ActiveRecord::Base
# This pattern supports cross-project references.
#
def self.reference_pattern
- %r{
+ @reference_pattern ||= %r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}
(?:
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index ef48207f956..bf185cb5dd8 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -135,6 +135,7 @@ class MergeRequest < ActiveRecord::Base
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
+ scope :from_project, ->(project) { where(source_project_id: project.id) }
scope :merged, -> { with_state(:merged) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
@@ -149,14 +150,14 @@ class MergeRequest < ActiveRecord::Base
#
# This pattern supports cross-project references.
def self.reference_pattern
- %r{
+ @reference_pattern ||= %r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<merge_request>\d+)
}x
end
def self.link_reference_pattern
- super("merge_requests", /(?<merge_request>\d+)/)
+ @link_reference_pattern ||= super("merge_requests", /(?<merge_request>\d+)/)
end
# Returns all the merge requests from an ActiveRecord:Relation.
@@ -279,7 +280,7 @@ class MergeRequest < ActiveRecord::Base
WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
def work_in_progress?
- title =~ WIP_REGEX
+ !!(title =~ WIP_REGEX)
end
def wipless_title
@@ -331,15 +332,15 @@ class MergeRequest < ActiveRecord::Base
# Returns the raw diff for this merge request
#
# see "git diff"
- def to_diff(current_user)
- target_project.repository.diff_text(target_branch, source_sha)
+ def to_diff
+ target_project.repository.diff_text(diff_base_commit.sha, source_sha)
end
# Returns the commit as a series of email patches.
#
# see "git format-patch"
- def to_patch(current_user)
- target_project.repository.format_patch(target_branch, source_sha)
+ def to_patch
+ target_project.repository.format_patch(diff_base_commit.sha, source_sha)
end
def hook_attrs
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index bbd59eab9ae..986184dd301 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -79,7 +79,7 @@ class Milestone < ActiveRecord::Base
end
def self.link_reference_pattern
- super("milestones", /(?<milestone>\d+)/)
+ @link_reference_pattern ||= super("milestones", /(?<milestone>\d+)/)
end
def self.upcoming
@@ -89,7 +89,7 @@ class Milestone < ActiveRecord::Base
def to_reference(from_project = nil)
escaped_title = self.title.gsub("]", "\\]")
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
url = h.namespace_project_milestone_url(self.project.namespace, self.project, self)
"[#{escaped_title}](#{url})"
diff --git a/app/models/note.rb b/app/models/note.rb
index b0c33f2eec5..87ced65c650 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -311,7 +311,7 @@ class Note < ActiveRecord::Base
for_merge_request? && for_diff_line?
end
- def for_project_snippet?
+ def for_snippet?
noteable_type == "Snippet"
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 2285063ab50..3e1f04b4158 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -206,6 +206,8 @@ class Project < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
# Scopes
+ default_scope { where(pending_delete: false) }
+
scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) }
scope :sorted_by_stars, -> { reorder('projects.star_count DESC') }
scope :sorted_by_names, -> { joins(:namespace).reorder('namespaces.name ASC, projects.name ASC') }
@@ -469,7 +471,7 @@ class Project < ActiveRecord::Base
end
def web_url
- Gitlab::Application.routes.url_helpers.namespace_project_url(self.namespace, self)
+ Gitlab::Routing.url_helpers.namespace_project_url(self.namespace, self)
end
def web_url_without_protocol
@@ -590,7 +592,7 @@ class Project < ActiveRecord::Base
if avatar.present?
[gitlab_config.url, avatar.url].join
elsif avatar_in_git
- Gitlab::Application.routes.url_helpers.namespace_project_avatar_url(namespace, self)
+ Gitlab::Routing.url_helpers.namespace_project_avatar_url(namespace, self)
end
end
@@ -929,16 +931,6 @@ class Project < ActiveRecord::Base
self.builds_enabled = true
end
- def unlink_fork
- if forked?
- forked_from_project.lfs_objects.find_each do |lfs_object|
- lfs_object.projects << self
- end
-
- forked_project_link.destroy
- end
- end
-
def any_runners?(&block)
if runners.active.any?(&block)
return true
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 05436cd0f79..eaa5654b9c6 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -20,7 +20,7 @@
#
class GitlabIssueTrackerService < IssueTrackerService
- include Gitlab::Application.routes.url_helpers
+ include Gitlab::Routing.url_helpers
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index aba37921c09..1ed42c4f3e7 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -21,7 +21,7 @@
class JiraService < IssueTrackerService
include HTTParty
- include Gitlab::Application.routes.url_helpers
+ include Gitlab::Routing.url_helpers
DEFAULT_API_VERSION = 2
diff --git a/app/models/project_services/slack_service/issue_message.rb b/app/models/project_services/slack_service/issue_message.rb
index 5af24a80609..438ff33fdff 100644
--- a/app/models/project_services/slack_service/issue_message.rb
+++ b/app/models/project_services/slack_service/issue_message.rb
@@ -22,7 +22,7 @@ class SlackService
@issue_url = obj_attr[:url]
@action = obj_attr[:action]
@state = obj_attr[:state]
- @description = obj_attr[:description]
+ @description = obj_attr[:description] || ''
end
def attachments
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c07e8072043..a8e826c9cbf 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -72,7 +72,7 @@ class Repository
return @has_visible_content unless @has_visible_content.nil?
@has_visible_content = cache.fetch(:has_visible_content?) do
- raw_repository.branch_count > 0
+ branch_count > 0
end
end
@@ -173,7 +173,7 @@ class Repository
end
def branch_names
- cache.fetch(:branch_names) { raw_repository.branch_names }
+ cache.fetch(:branch_names) { branches.map(&:name) }
end
def tag_names
@@ -191,7 +191,7 @@ class Repository
end
def branch_count
- @branch_count ||= cache.fetch(:branch_count) { raw_repository.branch_count }
+ @branch_count ||= cache.fetch(:branch_count) { branches.size }
end
def tag_count
@@ -239,7 +239,7 @@ class Repository
def expire_branches_cache
cache.expire(:branch_names)
- @branches = nil
+ @local_branches = nil
end
def expire_cache(branch_name = nil, revision = nil)
@@ -335,6 +335,8 @@ class Repository
# Runs code just before a repository is deleted.
def before_delete
+ expire_exists_cache
+
expire_cache if exists?
expire_root_ref_cache
@@ -362,6 +364,11 @@ class Repository
expire_tag_count_cache
end
+ def before_import
+ expire_emptiness_caches
+ expire_exists_cache
+ end
+
# Runs code after a repository has been forked/imported.
def after_import
expire_emptiness_caches
@@ -612,10 +619,14 @@ class Repository
refs_contains_sha('tag', sha)
end
- def branches
- @branches ||= raw_repository.branches
+ def local_branches
+ @local_branches ||= rugged.branches.each(:local).map do |branch|
+ Gitlab::Git::Branch.new(branch.name, branch.target)
+ end
end
+ alias_method :branches, :local_branches
+
def tags
@tags ||= raw_repository.tags
end
@@ -818,7 +829,7 @@ class Repository
end
def fetch_ref(source_path, source_ref, target_ref)
- args = %W(#{Gitlab.config.git.bin_path} fetch -f #{source_path} #{source_ref}:#{target_ref})
+ args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
Gitlab::Popen.popen(args, path_to_repo)
end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index b9e835a4486..b96e3937281 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -56,14 +56,14 @@ class Snippet < ActiveRecord::Base
#
# This pattern supports cross-project references.
def self.reference_pattern
- %r{
+ @reference_pattern ||= %r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<snippet>\d+)
}x
end
def self.link_reference_pattern
- super("snippets", /(?<snippet>\d+)/)
+ @link_reference_pattern ||= super("snippets", /(?<snippet>\d+)/)
end
def to_reference(from_project = nil)
diff --git a/app/models/user.rb b/app/models/user.rb
index 128ddc2a694..2b0bee2099f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -408,6 +408,8 @@ class User < ActiveRecord::Base
end
def owns_notification_email
+ return if self.temp_oauth_email?
+
self.errors.add(:notification_email, "is not an email you own") unless self.all_emails.include?(self.notification_email)
end
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index a5efb21fab6..82e7090f1ea 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -43,7 +43,7 @@ module Issues
def create_new_issue
new_params = { id: nil, iid: nil, label_ids: [], milestone: nil,
project: @new_project, author: @old_issue.author,
- description: unfold_references(@old_issue.description) }
+ description: rewrite_content(@old_issue.description) }
new_params = @old_issue.serializable_hash.merge(new_params)
CreateService.new(@new_project, @current_user, new_params).execute
@@ -53,7 +53,7 @@ module Issues
@old_issue.notes.find_each do |note|
new_note = note.dup
new_params = { project: @new_project, noteable: @new_issue,
- note: unfold_references(new_note.note),
+ note: rewrite_content(new_note.note),
created_at: note.created_at,
updated_at: note.updated_at }
@@ -61,6 +61,18 @@ module Issues
end
end
+ def rewrite_content(content)
+ return unless content
+
+ rewriters = [Gitlab::Gfm::ReferenceRewriter,
+ Gitlab::Gfm::UploadsRewriter]
+
+ rewriters.inject(content) do |text, klass|
+ rewriter = klass.new(text, @old_project, @current_user)
+ rewriter.rewrite(@new_project)
+ end
+ end
+
def close_issue
close_service = CloseService.new(@old_project, @current_user)
close_service.execute(@old_issue, notifications: false, system_note: false)
@@ -78,20 +90,12 @@ module Issues
direction: :to)
end
- def unfold_references(content)
- return unless content
-
- rewriter = Gitlab::Gfm::ReferenceRewriter.new(content, @old_project,
- @current_user)
- rewriter.rewrite(@new_project)
+ def mark_as_moved
+ @old_issue.update(moved_to: @new_issue)
end
def notify_participants
notification_service.issue_moved(@old_issue, @new_issue, @current_user)
end
-
- def mark_as_moved
- @old_issue.update(moved_to: @new_issue)
- end
end
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index 2015897dd19..ef15ef6a473 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -46,6 +46,8 @@ module Projects
def import_data
return unless has_importer?
+ project.repository.before_import
+
unless importer.execute
raise Error, 'The remote data could not be imported.'
end
diff --git a/app/services/projects/unlink_fork_service.rb b/app/services/projects/unlink_fork_service.rb
new file mode 100644
index 00000000000..315c3e16292
--- /dev/null
+++ b/app/services/projects/unlink_fork_service.rb
@@ -0,0 +1,19 @@
+module Projects
+ class UnlinkForkService < BaseService
+ def execute
+ return unless @project.forked?
+
+ @project.forked_from_project.lfs_objects.find_each do |lfs_object|
+ lfs_object.projects << @project
+ end
+
+ merge_requests = @project.forked_from_project.merge_requests.opened.from_project(@project)
+
+ merge_requests.each do |mr|
+ MergeRequests::CloseService.new(@project, @current_user).execute(mr)
+ end
+
+ @project.forked_project_link.destroy
+ end
+ end
+end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index ea2b26ccb52..f0615ec7420 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -95,17 +95,19 @@ class SystemHooksService
end
def project_member_data(model)
+ project = model.project || Project.unscoped.find(model.source_id)
+
{
- project_name: model.project.name,
- project_path: model.project.path,
- project_path_with_namespace: model.project.path_with_namespace,
- project_id: model.project.id,
- user_username: model.user.username,
- user_name: model.user.name,
- user_email: model.user.email,
- user_id: model.user.id,
- access_level: model.human_access,
- project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
+ project_name: project.name,
+ project_path: project.path,
+ project_path_with_namespace: project.path_with_namespace,
+ project_id: project.id,
+ user_username: model.user.username,
+ user_name: model.user.name,
+ user_email: model.user.email,
+ user_id: model.user.id,
+ access_level: model.human_access,
+ project_visibility: Project.visibility_levels.key(project.visibility_level_field).downcase
}
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index e022a046c48..658b086496f 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -224,7 +224,7 @@ class SystemNoteService
#
# "Started branch `issue-branch-button-201`"
def self.new_issue_branch(issue, project, author, branch)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
link = h.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch)
body = "Started branch [`#{branch}`](#{link})"
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index f2662922e90..42c5bca90fd 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -123,7 +123,7 @@ class TodoService
def handle_note(note, author)
# Skip system notes, and notes on project snippet
- return if note.system? || note.for_project_snippet?
+ return if note.system? || note.for_snippet?
project = note.project
target = note.noteable
@@ -170,14 +170,30 @@ class TodoService
end
def filter_mentioned_users(project, target, author)
- mentioned_users = target.mentioned_users.select do |user|
- user.can?(:read_project, project)
- end
-
+ mentioned_users = target.mentioned_users
+ mentioned_users = reject_users_without_access(mentioned_users, project, target)
mentioned_users.delete(author)
mentioned_users.uniq
end
+ def reject_users_without_access(users, project, target)
+ if target.is_a?(Note) && target.for_issue?
+ target = target.noteable
+ end
+
+ if target.is_a?(Issue)
+ select_users(users, :read_issue, target)
+ else
+ select_users(users, :read_project, project)
+ end
+ end
+
+ def select_users(users, ability, subject)
+ users.select do |user|
+ user.can?(ability.to_sym, subject)
+ end
+ end
+
def pending_todos(user, criteria = {})
valid_keys = [:project_id, :target_id, :target_type, :commit_id]
user.todos.pending.where(criteria.slice(*valid_keys))
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 86d24469e05..1af9e9b0edb 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -1,14 +1,15 @@
# encoding: utf-8
class FileUploader < CarrierWave::Uploader::Base
include UploaderHelper
+ MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}
storage :file
attr_accessor :project, :secret
- def initialize(project, secret = self.class.generate_secret)
+ def initialize(project, secret = nil)
@project = project
- @secret = secret
+ @secret = secret || self.class.generate_secret
end
def base_dir
@@ -23,14 +24,14 @@ class FileUploader < CarrierWave::Uploader::Base
File.join(base_dir, 'tmp', @project.path_with_namespace, @secret)
end
- def self.generate_secret
- SecureRandom.hex
- end
-
def secure_url
File.join("/uploads", @secret, file.filename)
end
+ def to_markdown
+ to_h[:markdown]
+ end
+
def to_h
filename = image? ? self.file.basename : self.file.filename
escaped_filename = filename.gsub("]", "\\]")
@@ -45,4 +46,8 @@ class FileUploader < CarrierWave::Uploader::Base
markdown: markdown
}
end
+
+ def self.generate_secret
+ SecureRandom.hex
+ end
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 0350995d03d..de86dacbb12 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -77,13 +77,6 @@
= f.check_box :gravatar_enabled
Gravatar enabled
.form-group
- .col-sm-offset-2.col-sm-10
- .checkbox
- = f.label :twitter_sharing_enabled do
- = f.check_box :twitter_sharing_enabled, :'aria-describedby' => 'twitter_help_block'
- Twitter enabled
- %span.help-block#twitter_help_block Show users a button to share their newly created public or internal projects on twitter
- .form-group
= f.label :default_projects_limit, class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :default_projects_limit, class: 'form-control'
diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml
index 588ad767426..3571eefd570 100644
--- a/app/views/admin/builds/_build.html.haml
+++ b/app/views/admin/builds/_build.html.haml
@@ -15,7 +15,7 @@
%td
- if project
- = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project), class: "monospace"
+ = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
%td
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 3274ba5377b..6dd2fef395d 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -1,4 +1,4 @@
-.admin-dashboard
+.admin-dashboard.prepend-top-default
.row
.col-md-4
%h4 Statistics
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 41c43899978..149593e7f46 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -1,5 +1,5 @@
- page_title "Deploy Keys"
-.panel.panel-default
+.panel.panel-default.prepend-top-default
.panel-heading
Public deploy keys (#{@deploy_keys.count})
.controls
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
new file mode 100644
index 00000000000..9025aaac097
--- /dev/null
+++ b/app/views/admin/groups/_group.html.haml
@@ -0,0 +1,28 @@
+- css_class = '' unless local_assigns[:css_class]
+- css_class += ' no-description' if group.description.blank?
+
+%li.group-row{ class: css_class }
+ .controls.hidden-xs
+ = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: 'btn btn-grouped btn-sm'
+ = link_to 'Destroy', [:admin, group], data: {confirm: "REMOVE #{group.name}? Are you sure?"}, method: :delete, class: 'btn btn-grouped btn-sm btn-remove'
+
+ .stats
+ %span
+ = icon('bookmark')
+ = number_with_delimiter(group.projects.count)
+
+ %span
+ = icon('users')
+ = number_with_delimiter(group.users.count)
+
+ %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
+ = visibility_level_icon(group.visibility_level, fw: false)
+
+ = image_tag group_icon(group), class: 'avatar s40 hidden-xs'
+ .title
+ = link_to [:admin, group], class: 'group-name' do
+ = group.name
+
+ - if group.description.present?
+ .description
+ = markdown(group.description, pipeline: :description)
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 6bdc885a312..775072a7441 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -1,20 +1,19 @@
- page_title "Groups"
%h3.page-title
Groups (#{number_with_delimiter(@groups.total_count)})
- = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
%p.light
Group allows you to keep projects organized.
Use groups for uniting related projects.
-%hr
-= form_tag admin_groups_path, method: :get, class: 'form-inline' do
- = hidden_field_tag :sort, @sort
- .form-group
- = text_field_tag :name, params[:name], class: "form-control"
- = button_tag "Search", class: "btn submit btn-primary"
+.top-area
+ .nav-search
+ = form_tag admin_groups_path, method: :get, class: 'form-inline' do
+ = hidden_field_tag :sort, @sort
+ = text_field_tag :name, params[:name], class: "form-control"
+ = button_tag "Search", class: "btn submit btn-primary"
- .pull-right
+ .nav-controls
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light
@@ -33,34 +32,10 @@
= sort_title_recently_updated
= link_to admin_groups_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
+ = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
-%hr
-
-%ul.bordered-list
+%ul.content-list
- @groups.each do |group|
- %li
- .clearfix
- .pull-right.prepend-top-10
- = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-sm"
- = link_to 'Destroy', [:admin, group], data: {confirm: "REMOVE #{group.name}? Are you sure?"}, method: :delete, class: "btn btn-sm btn-remove"
-
- %h4
- = link_to [:admin, group] do
- %span{ class: visibility_level_color(group.visibility_level) }
- = visibility_level_icon(group.visibility_level)
-
- %i.fa.fa-folder
- = group.name
-
- &rarr;
- %span.monospace
- %strong #{group.path}/
- .clearfix
- %p
- = truncate group.description, length: 150
- .clearfix
- %p.light
- #{pluralize(group.members.size, 'member')}, #{pluralize(group.projects.count, 'project')}
-
+ = render 'group', group: group
= paginate @groups, theme: "gitlab"
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
index 3c57e3dc174..05d6b9ed238 100644
--- a/app/views/admin/labels/index.html.haml
+++ b/app/views/admin/labels/index.html.haml
@@ -1,8 +1,10 @@
- page_title "Labels"
-= link_to new_admin_label_path, class: "pull-right btn btn-nr btn-new" do
- New label
-%h3.page-title
- Labels
+
+%div
+ = link_to new_admin_label_path, class: "pull-right btn btn-nr btn-new" do
+ New label
+ %h3.page-title
+ Labels
%hr
.labels
@@ -13,4 +15,4 @@
- else
.light-well
.nothing-here-block There are no labels yet
-
+
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index c407972cd08..2dad64b8d0f 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -1,4 +1,4 @@
-%p.lead
+%p.lead.prepend-top-default
%span
To register a new runner you should enter the following registration token.
With this token the runner will request a unique runner token and use that for future communication.
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index e3a4d64df01..aa0aff86d4d 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -1,4 +1,4 @@
-%li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo) }
+%li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data:{url: todo_target_path(todo)} }
.todo-item.todo-block
= image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:''
@@ -10,7 +10,10 @@
(removed)
%span.todo-label
= todo_action_name(todo)
- = todo_target_link(todo)
+ - if todo.target
+ = todo_target_link(todo)
+ - else
+ (removed)
&middot; #{time_ago_with_tooltip(todo.created_at)}
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 42c2764e7e2..4d20dd5830e 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -1,5 +1,5 @@
- if event.visible_to_user?(current_user)
- .event-item{class: "#{event.body? ? "event-block" : "event-inline" }"}
+ .event-item{ class: event_row_class(event) }
.event-item-timestamp
#{time_ago_with_tooltip(event.created_at)}
diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml
index 8cf36c711b4..5a2a469ba62 100644
--- a/app/views/events/event/_created_project.html.haml
+++ b/app/views/events/event/_created_project.html.haml
@@ -7,21 +7,3 @@
= link_to_project event.project
- else
= event.project_name
-
-- if !event.project.private? && twitter_sharing_enabled?
- .event-body{"data-user-is" => event.author_id}
- .event-note
- .md
- %p
- Congratulations! Why not share your accomplishment with the world?
-
- %a.twitter-share-button{ |
- href: "https://twitter.com/share", |
- "data-url" => event.project.web_url, |
- "data-text" => "I just #{event.action_name} a new project on GitLab! GitLab is version control on your server.", |
- "data-size" => "medium", |
- "data-related" => "gitlab", |
- "data-hashtags" => "gitlab", |
- "data-count" => "none"}
- Tweet
- %script{src: "//platform.twitter.com/widgets.js"}
diff --git a/app/views/layouts/_collapse_button.html.haml b/app/views/layouts/_collapse_button.html.haml
deleted file mode 100644
index 2ed51d87ca1..00000000000
--- a/app/views/layouts/_collapse_button.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-- if nav_menu_collapsed?
- = link_to icon('angle-right'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
-- else
- = link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index c799e9c588d..9be36273c7d 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,5 +1,7 @@
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
= render "layouts/broadcast"
+ .expand-nav
+ = link_to icon('bars'), '#', class: 'toggle-nav-collapse', title: "Open sidebar"
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo
%a#logo
@@ -8,15 +10,19 @@
.gitlab-text-container
%h3 GitLab
- - if defined?(sidebar) && sidebar
- = render "layouts/nav/#{sidebar}"
- - elsif current_user
- = render 'layouts/nav/dashboard'
+ - primary_sidebar = current_user ? 'dashboard' : 'explore'
+
+ - if defined?(sidebar) && sidebar && sidebar != primary_sidebar
+ .complex-sidebar
+ .nav-primary
+ = render "layouts/nav/#{primary_sidebar}"
+ .nav-secondary
+ = render "layouts/nav/#{sidebar}"
- else
- = render 'layouts/nav/explore'
+ = render "layouts/nav/#{primary_sidebar}"
.collapse-nav
- = render partial: 'layouts/collapse_button'
+ = link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Hide sidebar"
- if current_user
= link_to current_user, class: 'sidebar-user', title: "Profile" do
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 54af2c3063c..9d4ab9847a8 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -1,10 +1,33 @@
-.search
- = form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f|
- = search_field_tag "search", nil, placeholder: 'Search', class: "search-input form-control", spellcheck: false, tabindex: "1"
+- if controller.controller_path =~ /^groups/
+ - label = 'This group'
+- if controller.controller_path =~ /^projects/
+ - label = 'This project'
+
+.search.search-form{class: "#{'has-location-badge' if label.present?}"}
+ = form_tag search_path, method: :get, class: 'navbar-form' do |f|
+ .search-input-container
+ .search-location-badge
+ - if label.present?
+ %span.location-badge
+ %i.location-text
+ = label
+ .search-input-wrap
+ .dropdown{ data: {url: search_autocomplete_path } }
+ = search_field_tag "search", nil, placeholder: 'Search', class: "search-input dropdown-menu-toggle", spellcheck: false, tabindex: "1", autocomplete: 'off', data: { toggle: 'dropdown' }
+ .dropdown-menu.dropdown-select
+ = dropdown_content do
+ %ul
+ %li
+ %a.is-focused.dropdown-menu-empty-link
+ Loading...
+ = dropdown_loading
+ %i.search-icon
+ %i.clear-icon.js-clear-input
+
= hidden_field_tag :group_id, @group.try(:id)
- - if @project && @project.persisted?
- = hidden_field_tag :project_id, @project.id
+ = hidden_field_tag :project_id, @project && @project.persisted? ? @project.id : '', id: 'search_project_id'
+ - if @project && @project.persisted?
- if current_controller?(:issues)
= hidden_field_tag :scope, 'issues'
- elsif current_controller?(:merge_requests)
@@ -21,10 +44,3 @@
= hidden_field_tag :repository_ref, @ref
= button_tag 'Go' if ENV['RAILS_ENV'] == 'test'
.search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref }
-
-:javascript
- $('.search-input').on('keyup', function(e) {
- if (e.keyCode == 27) {
- $('.search-input').blur();
- }
- });
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 280a1b93729..22d1d4d8597 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -95,7 +95,7 @@
Spam Logs
%span.count= number_with_delimiter(SpamLog.count(:all))
- = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
+ = nav_link(controller: :application_settings) do
= link_to admin_application_settings_path, title: 'Settings' do
= icon('cogs fw')
%span
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 4a0069f18f8..d1a180e4299 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -15,12 +15,12 @@
= icon('dashboard fw')
%span
Activity
- = nav_link(controller: :groups) do
+ = nav_link(path: ['dashboard/groups#index', 'explore/groups#index']) do
= link_to dashboard_groups_path, title: 'Groups' do
= icon('group fw')
%span
Groups
- = nav_link(controller: :milestones) do
+ = nav_link(path: 'dashboard#milestones') do
= link_to dashboard_milestones_path, title: 'Milestones' do
= icon('clock-o fw')
%span
@@ -48,7 +48,6 @@
%span
Help
- %li.separate-item
= nav_link(controller: :profile) do
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
= icon('user fw')
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 55940741dc0..0b7de9633ec 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,12 +1,4 @@
%ul.nav.nav-sidebar
- = nav_link do
- = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
- = icon('caret-square-o-left fw')
- %span
- Go to dashboard
-
- %li.separate-item
-
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do
= icon('group fw')
@@ -42,7 +34,7 @@
%span
Members
- if can?(current_user, :admin_group, @group)
- = nav_link(html_options: { class: "separate-item" }) do
+ = nav_link do
= link_to edit_group_path(@group), title: 'Settings' do
= icon ('cogs fw')
%span
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 3b9d31a6fc5..cc119fd64e6 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,12 +1,4 @@
%ul.nav.nav-sidebar
- = nav_link do
- = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
- = icon('caret-square-o-left fw')
- %span
- Go to dashboard
-
- %li.separate-item
-
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do
= icon('user fw')
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 86b46e8c75e..d0f82b5f57f 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,19 +1,4 @@
%ul.nav.nav-sidebar
- - if @project.group
- = nav_link do
- = link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do
- = icon('caret-square-o-left fw')
- %span
- Go to group
- - else
- = nav_link do
- = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
- = icon('caret-square-o-left fw')
- %span
- Go to dashboard
-
- %li.separate-item
-
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
= icon('bookmark fw')
@@ -113,7 +98,7 @@
Snippets
- if project_nav_tab? :settings
- = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
+ = nav_link(html_options: {class: "#{project_tab_class}"}) do
= link_to edit_project_path(@project), title: 'Settings' do
= icon('cogs fw')
%span
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index de80abd7f4d..3d15c0d932b 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -56,19 +56,20 @@
.prepend-top-default
= f.submit 'Update settings', class: "btn btn-create"
%hr
- %h5
- Groups (#{@group_members.count})
- %div
- %ul.bordered-list
- - @group_members.each do |group_member|
- - notification = Notification.new(group_member)
- = render 'settings', type: 'group', membership: group_member, notification: notification
- %h5
- Projects (#{@project_members.count})
- %p.account-well
- To specify the notification level per project of a group you belong to, you need to be a member of the project itself, not only its group.
- .append-bottom-default
- %ul.bordered-list
- - @project_members.each do |project_member|
- - notification = Notification.new(project_member)
- = render 'settings', type: 'project', membership: project_member, notification: notification
+.col-lg-9.col-lg-push-3
+ %h5
+ Groups (#{@group_members.count})
+ %div
+ %ul.bordered-list
+ - @group_members.each do |group_member|
+ - notification = Notification.new(group_member)
+ = render 'settings', type: 'group', membership: group_member, notification: notification
+ %h5
+ Projects (#{@project_members.count})
+ %p.account-well
+ To specify the notification level per project of a group you belong to, you need to be a member of the project itself, not only its group.
+ .append-bottom-default
+ %ul.bordered-list
+ - @project_members.each do |project_member|
+ - notification = Notification.new(project_member)
+ = render 'settings', type: 'project', membership: project_member, notification: notification
diff --git a/app/views/profiles/two_factor_auths/new.html.haml b/app/views/profiles/two_factor_auths/new.html.haml
index 5d342ef58e5..69fc81cb45c 100644
--- a/app/views/profiles/two_factor_auths/new.html.haml
+++ b/app/views/profiles/two_factor_auths/new.html.haml
@@ -8,8 +8,6 @@
Increase your account's security by enabling two-factor authentication (2FA).
.col-lg-9
%p
- Status: #{current_user.two_factor_enabled? ? 'enabled' : 'disabled'}
- %p
Download the Google Authenticator application from App Store for iOS or Google Play for Android and scan this code.
More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}.
.row.append-bottom-10
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index e7c85edff96..1e4c46fca2f 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -3,25 +3,32 @@
%a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('plus')
%ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
- - if can?(current_user, :create_issue, @project)
+ - can_create_issue = can?(current_user, :create_issue, @project)
+ - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
+ - can_create_snippet = can?(current_user, :create_snippet, @project)
+
+ - if can_create_issue
%li
= link_to url_for_new_issue(@project, only_path: true) do
= icon('exclamation-circle fw')
New issue
- - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
+
- if merge_project
%li
= link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project) do
= icon('tasks fw')
New merge request
- - if can?(current_user, :create_snippet, @project)
+
+ - if can_create_snippet
%li
= link_to new_namespace_project_snippet_path(@project.namespace, @project) do
= icon('file-text-o fw')
New snippet
- - if can?(current_user, :push_code, @project)
+ - if can_create_issue || merge_project || can_create_snippet
%li.divider
+
+ - if can?(current_user, :push_code, @project)
%li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do
= icon('file fw')
@@ -35,13 +42,11 @@
= icon('tags fw')
New tag
- elsif current_user && current_user.already_forked?(@project)
- %li.divider
%li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do
= icon('file fw')
New file
- elsif can?(current_user, :fork_project, @project)
- %li.divider
%li
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'),
notice: edit_in_new_fork_notice,
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index d22d1da8402..2cf9115e4dd 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -39,7 +39,7 @@
%td
= build.name
- .pull-right
+ .label-container
- if build.tags.any?
- build.tags.each do |tag|
%span.label.label-primary
diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml
index 8367112a9cb..2731219ccad 100644
--- a/app/views/projects/diffs/_image.html.haml
+++ b/app/views/projects/diffs/_image.html.haml
@@ -1,7 +1,10 @@
- diff = diff_file.diff
- file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path))
-- old_commit_id = diff_refs.first.id
-- old_file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(old_commit_id, diff.old_path))
+// diff_refs will be nil for orphaned commits (e.g. first commit in repo)
+- if diff_refs
+ - old_commit_id = diff_refs.first.id
+ - old_file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(old_commit_id, diff.old_path))
+
- if diff.renamed_file || diff.new_file || diff.deleted_file
.image
%span.wrap
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index b05ab869215..2ec0d20a879 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -1,15 +1,17 @@
- if @ci_commit
.mr-widget-heading
- .ci_widget{class: "ci-#{@ci_commit.status}"}
- = ci_status_icon(@ci_commit)
- %span
- Build
- = ci_status_label(@ci_commit)
- for
- = succeed "." do
- = link_to @ci_commit.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @ci_commit.sha), class: "monospace"
- %span.ci-coverage
- = link_to "View details", builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "js-show-tab", data: {action: 'builds'}
+ - %w[success skipped canceled failed running pending].each do |status|
+ .ci_widget{ class: "ci-#{status}", style: ("display:none" unless @ci_commit.status == status) }
+ = ci_icon_for_status(status)
+ %span
+ CI build
+ = ci_label_for_status(status)
+ for
+ - commit = @merge_request.last_commit
+ = succeed "." do
+ = link_to @ci_commit.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @ci_commit.sha), class: "monospace"
+ %span.ci-coverage
+ = link_to "View details", builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "js-show-tab", data: {action: 'builds'}
- elsif @merge_request.has_ci?
- # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
@@ -43,5 +45,5 @@
:javascript
$(function() {
- merge_request_widget.getCiStatus();
+ merge_request_widget.getCIStatus(false);
});
diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml
index a489d4f9b24..2be06aebe6c 100644
--- a/app/views/projects/merge_requests/widget/_show.html.haml
+++ b/app/views/projects/merge_requests/widget/_show.html.haml
@@ -9,12 +9,17 @@
:javascript
var merge_request_widget;
-
- merge_request_widget = new MergeRequestWidget({
- url_to_automerge_check: "#{merge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
+ var opts = {
+ merge_check_url: "#{merge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
check_enable: #{@merge_request.unchecked? ? "true" : "false"},
- url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
+ ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
+ gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
+ ci_status: "",
+ ci_message: "Build {{status}} for \"{{title}}\"",
ci_enable: #{@project.ci_service ? "true" : "false"},
- current_status: "#{@merge_request.gitlab_merge_status}",
- });
+ builds_path: "#{builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}"
+ };
+ if(typeof merge_request_widget === 'undefined') {
+ merge_request_widget = new MergeRequestWidget(opts);
+ }
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 2cf32e6093d..34fe1743f4b 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -5,28 +5,21 @@
= image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
.timeline-content
.note-header
+ = link_to_member(note.project, note.author, avatar: false)
+ .inline.note-headline-light
+ = "#{note.author.to_reference} commented"
+ %a{ href: "##{dom_id(note)}" }
+ = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
- if note_editable?(note)
.note-actions
- = link_to '#', title: 'Edit comment', class: 'js-note-edit' do
+ - access = note.project.team.human_max_access(note.author.id)
+ - if access
+ %span.note-role
+ = access
+ = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do
= icon('pencil-square-o')
-
- = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'js-note-delete danger' do
+ = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger' do
= icon('trash-o')
-
- - unless note.system
- - access = note.project.team.human_max_access(note.author.id)
- - if access
- %span.note-role.label
- = access
-
- = link_to_member(note.project, note.author, avatar: false)
-
- %span.author-username
- = '@' + note.author.username
-
- %span.note-last-update
- %a{name: dom_id(note), href: "##{dom_id(note)}", title: 'Link here'}
- = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago')
.note-body{class: note_editable?(note) ? 'js-task-list-container' : ''}
.note-text
= preserve do
diff --git a/app/views/projects/notes/discussions/_active.html.haml b/app/views/projects/notes/discussions/_active.html.haml
index 4f15a99d061..cd8a5f0bd02 100644
--- a/app/views/projects/notes/discussions/_active.html.haml
+++ b/app/views/projects/notes/discussions/_active.html.haml
@@ -1,22 +1,20 @@
- note = discussion_notes.first
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
+ = link_to_member(@project, note.author, avatar: false)
+ .inline.discussion-headline-light
+ = "#{note.author.to_reference} started a discussion"
+ = link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do
+ on the diff
.discussion-actions
- = link_to "#", class: "js-toggle-button" do
+ = link_to "#", class: "discussion-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-up
Show/hide discussion
- %div
- = link_to_member(@project, note.author, avatar: false)
- started a discussion
- = link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do
- %strong on the diff
.last-update.hide.js-toggle-content
- last_note = discussion_notes.last
last updated by
= link_to_member(@project, last_note.author, avatar: false)
-
- %span.discussion-last-update
- #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
+ #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
diff --git a/app/views/projects/notes/discussions/_commit.html.haml b/app/views/projects/notes/discussions/_commit.html.haml
index 3da2f2060b8..46f2ba4bbcf 100644
--- a/app/views/projects/notes/discussions/_commit.html.haml
+++ b/app/views/projects/notes/discussions/_commit.html.haml
@@ -1,20 +1,22 @@
- note = discussion_notes.first
+- commit = note.noteable
+- commit_description = commit ? 'commit' : 'a deleted commit'
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
+ = link_to_member(@project, note.author, avatar: false)
+ .inline.discussion-headline-light
+ = "#{note.author.to_reference} started a discussion on #{commit_description}"
+ - if commit
+ = link_to(commit.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable), class: 'monospace')
.discussion-actions
- = link_to "#", class: "js-toggle-button" do
+ = link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-up
Show/hide discussion
- %div
- = link_to_member(@project, note.author, avatar: false)
- started a discussion on commit
- = link_to(note.noteable.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable), class: 'monospace')
.last-update.hide.js-toggle-content
- last_note = discussion_notes.last
last updated by
= link_to_member(@project, last_note.author, avatar: false)
- %span.discussion-last-update
- #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
+ #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content
- if note.for_diff_line?
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
diff --git a/app/views/projects/notes/discussions/_outdated.html.haml b/app/views/projects/notes/discussions/_outdated.html.haml
index 218b0da3977..f8e000b424f 100644
--- a/app/views/projects/notes/discussions/_outdated.html.haml
+++ b/app/views/projects/notes/discussions/_outdated.html.haml
@@ -1,19 +1,18 @@
- note = discussion_notes.first
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
+ = link_to_member(@project, note.author, avatar: false)
+ .inline.discussion-headline-light
+ = "#{note.author.to_reference} started a discussion"
+ on the outdated diff
.discussion-actions
- = link_to "#", class: "js-toggle-button" do
+ = link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-down
Show/hide discussion
- %div
- = link_to_member(@project, note.author, avatar: false)
- started a discussion on the
- %strong outdated diff
- %div
+ .last-update.hide.js-toggle-content
- last_note = discussion_notes.last
last updated by
= link_to_member(@project, last_note.author, avatar: false)
- %span.discussion-last-update
- #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
+ #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content.hide
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml
index 5fcba2b7e93..9544e3d3e17 100644
--- a/app/views/search/results/_note.html.haml
+++ b/app/views/search/results/_note.html.haml
@@ -1,24 +1,20 @@
- project = note.project
+- note_url = Gitlab::UrlBuilder.new(:note).build(note.id)
+- noteable_identifier = note.noteable.try(:iid) || note.noteable.id
.search-result-row
%h5.note-search-caption.str-truncated
%i.fa.fa-comment
= link_to_member(project, note.author, avatar: false)
commented on
+ = link_to project.name_with_namespace, project
+ &middot;
- if note.for_commit?
- = link_to project do
- = project.name_with_namespace
- &middot;
- = link_to namespace_project_commit_path(project.namespace, project, note.commit_id, anchor: dom_id(note)) do
- Commit #{truncate_sha(note.commit_id)}
+ = link_to "Commit #{truncate_sha(note.commit_id)}", note_url
- else
- = link_to project do
- = project.name_with_namespace
- &middot;
- %span #{note.noteable_type.titleize} ##{note.noteable.iid}
+ %span #{note.noteable_type.titleize} ##{noteable_identifier}
&middot;
- = link_to [project.namespace.becomes(Namespace), project, note.noteable, anchor: dom_id(note)] do
- = note.noteable.title
+ = link_to note.noteable.title, note_url
.note-search-result
.term
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index c99da92be9f..921eaefd79a 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -7,13 +7,13 @@
class: "check_all_issues left"
.issues-other-filters
.filter-item.inline
- - if params[:author_id]
+ - if params[:author_id].present?
= hidden_field_tag(:author_id, params[:author_id])
= dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit",
placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
.filter-item.inline
- - if params[:assignee_id]
+ - if params[:assignee_id].present?
= hidden_field_tag(:assignee_id, params[:assignee_id])
= dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 006a34a11e3..fd5e58c1f1f 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -1,4 +1,4 @@
-- if params[:label_name]
+- if params[:label_name].present?
= hidden_field_tag(:label_name, params[:label_name])
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-filter-submit{type: "button", data: {toggle: "dropdown", field_name: "label_name", show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}}
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index 1c79494f816..2fcf40ece99 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -1,7 +1,7 @@
-- if params[:milestone_title]
+- if params[:milestone_title].present?
= hidden_field_tag(:milestone_title, params[:milestone_title])
-= dropdown_tag(h(params[:milestone_title].presence || "Milestone"), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit js-extra-options', filter: true, dropdown_class: "dropdown-menu-selectable",
- placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
+= dropdown_tag(milestone_dropdown_label(params[:milestone_title]), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
+ placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, show_upcoming: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if @project
%ul.dropdown-footer-list
- if can? current_user, :admin_milestone, @project
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 56f19ef3d6b..c4640e16dd7 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -150,3 +150,4 @@
new LabelsSelect();
new IssuableContext('#{current_user.to_json(only: [:username, :id, :name])}');
new Subscription('.subscription')
+ new Sidebar(); \ No newline at end of file
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index bca816f22cb..0c4b6a5618b 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -87,7 +87,7 @@
%div{ class: container_class }
.tab-content
#activity.tab-pane
- .gray-content-block.white.second-block
+ .gray-content-block.calender-block.white.second-block.hidden-xs
%div{ class: container_class }
.user-calendar{data: {href: user_calendar_path}}
%h4.center.light
diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml
index 02647229776..8ffcdc4a327 100644
--- a/app/views/votes/_votes_block.html.haml
+++ b/app/views/votes/_votes_block.html.haml
@@ -15,12 +15,14 @@
- if current_user
:javascript
+ var get_emojis_url = "#{emojis_path}";
var post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}";
var noteable_type = "#{votable.class.name.underscore}";
var noteable_id = "#{votable.id}";
var aliases = #{AwardEmoji.aliases.to_json};
window.awards_handler = new AwardsHandler(
+ get_emojis_url,
post_emoji_url,
noteable_type,
noteable_id,
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index 55cb6af232e..ccefd0f71a0 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -5,6 +5,9 @@ class ProjectCacheWorker
def perform(project_id)
project = Project.find(project_id)
+
+ return unless project.repository.exists?
+
project.update_repository_size
project.update_commit_count
diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb
index d06e4480292..b51c6a266c9 100644
--- a/app/workers/project_destroy_worker.rb
+++ b/app/workers/project_destroy_worker.rb
@@ -5,7 +5,7 @@ class ProjectDestroyWorker
def perform(project_id, user_id, params)
begin
- project = Project.find(project_id)
+ project = Project.unscoped.find(project_id)
rescue ActiveRecord::RecordNotFound
return
end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 500b745f55e..fb1c3476f65 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -106,7 +106,7 @@ production: &base
enabled: false
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The `%{key}` placeholder is added after the user part, after a `+` character, before the `@`.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 626268d7648..2b989015279 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -174,7 +174,6 @@ end
Settings.gitlab['time_zone'] ||= nil
Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil?
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
-Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
diff --git a/config/mail_room.yml b/config/mail_room.yml
index aed55f74eab..60257329f3e 100644
--- a/config/mail_room.yml
+++ b/config/mail_room.yml
@@ -17,7 +17,7 @@ if File.exists?(config_file)
config['start_tls'] = false if config['start_tls'].nil?
config['mailbox'] = "inbox" if config['mailbox'].nil?
- if config['enabled'] && config['address'] && config['address'].include?('%{key}')
+ if config['enabled'] && config['address']
redis_url = Gitlab::RedisConfig.new(rails_env).url
%>
-
diff --git a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
new file mode 100644
index 00000000000..1fff9759d1e
--- /dev/null
+++ b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb
@@ -0,0 +1,17 @@
+class RemoveTodosForDeletedIssues < ActiveRecord::Migration
+ def up
+ execute <<-SQL
+ DELETE FROM todos
+ WHERE todos.target_type = 'Issue'
+ AND NOT EXISTS (
+ SELECT *
+ FROM issues
+ WHERE issues.id = todos.target_id
+ AND issues.deleted_at IS NULL
+ )
+ SQL
+ end
+
+ def down
+ end
+end
diff --git a/db/migrate/20160329144452_add_index_on_pending_delete_projects.rb b/db/migrate/20160329144452_add_index_on_pending_delete_projects.rb
new file mode 100644
index 00000000000..275554e736e
--- /dev/null
+++ b/db/migrate/20160329144452_add_index_on_pending_delete_projects.rb
@@ -0,0 +1,6 @@
+class AddIndexOnPendingDeleteProjects < ActiveRecord::Migration
+ def change
+ add_index :projects, :pending_delete
+ end
+end
+
diff --git a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
new file mode 100644
index 00000000000..54cea964ff2
--- /dev/null
+++ b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb
@@ -0,0 +1,17 @@
+class RemoveTodosForDeletedMergeRequests < ActiveRecord::Migration
+ def up
+ execute <<-SQL
+ DELETE FROM todos
+ WHERE todos.target_type = 'MergeRequest'
+ AND NOT EXISTS (
+ SELECT *
+ FROM merge_requests
+ WHERE merge_requests.id = todos.target_id
+ AND merge_requests.deleted_at IS NULL
+ )
+ SQL
+ end
+
+ def down
+ end
+end
diff --git a/db/migrate/20160331223143_remove_twitter_sharing_enabled_from_application_settings.rb b/db/migrate/20160331223143_remove_twitter_sharing_enabled_from_application_settings.rb
new file mode 100644
index 00000000000..0d736e323b6
--- /dev/null
+++ b/db/migrate/20160331223143_remove_twitter_sharing_enabled_from_application_settings.rb
@@ -0,0 +1,5 @@
+class RemoveTwitterSharingEnabledFromApplicationSettings < ActiveRecord::Migration
+ def change
+ remove_column :application_settings, :twitter_sharing_enabled, :boolean
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index dce2bfe62ca..e63e22ce864 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160320204112) do
+ActiveRecord::Schema.define(version: 20160331133914) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -418,7 +418,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do
t.integer "iid"
t.integer "updated_by_id"
t.integer "moved_to_id"
- t.boolean "confidential", default: false
+ t.boolean "confidential", default: false
t.datetime "deleted_at"
end
@@ -745,6 +745,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do
add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
add_index "projects", ["path"], name: "index_projects_on_path", using: :btree
add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
+ add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree
add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree
add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
diff --git a/doc/api/labels.md b/doc/api/labels.md
index 6496ffe9fd1..544e898b6aa 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -8,9 +8,9 @@ Get all labels for a given project.
GET /projects/:id/labels
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | --------------------- |
+| `id` | integer | yes | The ID of the project |
```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/1/labels
@@ -22,35 +22,43 @@ Example response:
[
{
"name" : "bug",
- "color" : "#d9534f"
+ "color" : "#d9534f",
+ "description": "Bug reported by user"
},
{
"color" : "#d9534f",
- "name" : "confirmed"
+ "name" : "confirmed",
+ "description": "Confirmed issue"
},
{
"name" : "critical",
- "color" : "#d9534f"
+ "color" : "#d9534f",
+ "description": "Criticalissue. Need fix ASAP"
},
{
"color" : "#428bca",
- "name" : "discussion"
+ "name" : "discussion",
+ "description": "Issue that needs further discussion"
},
{
"name" : "documentation",
- "color" : "#f0ad4e"
+ "color" : "#f0ad4e",
+ "description": "Issue about documentation"
},
{
"color" : "#5cb85c",
- "name" : "enhancement"
+ "name" : "enhancement",
+ "description": "Enhancement proposal"
},
{
"color" : "#428bca",
- "name" : "suggestion"
+ "name" : "suggestion",
+ "description": "Suggestion"
},
{
"color" : "#f0ad4e",
- "name" : "support"
+ "name" : "support",
+ "description": "Support issue"
}
]
```
@@ -66,11 +74,12 @@ and 409 if the label already exists.
POST /projects/:id/labels
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
-| `name` | string | yes | The name of the label |
-| `color` | string | yes | The color of the label in 6-digit hex notation with leading `#` sign |
+| Attribute | Type | Required | Description |
+| ------------- | ------- | -------- | ---------------------------- |
+| `id` | integer | yes | The ID of the project |
+| `name` | string | yes | The name of the label |
+| `color` | string | yes | The color of the label in 6-digit hex notation with leading `#` sign |
+| `description` | string | no | The description of the label |
```bash
curl --data "name=feature&color=#5843AD" -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/labels"
@@ -81,7 +90,8 @@ Example response:
```json
{
"name" : "feature",
- "color" : "#5843AD"
+ "color" : "#5843AD",
+ "description":null
}
```
@@ -97,10 +107,10 @@ In case of an error, an additional error message is returned.
DELETE /projects/:id/labels
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
-| `name` | string | yes | The name of the label |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | --------------------- |
+| `id` | integer | yes | The ID of the project |
+| `name` | string | yes | The name of the label |
```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/labels?name=bug"
@@ -112,6 +122,7 @@ Example response:
{
"title" : "feature",
"color" : "#5843AD",
+ "description": "New feature proposal",
"updated_at" : "2015-11-03T21:22:30.737Z",
"template" : false,
"project_id" : 1,
@@ -133,15 +144,16 @@ In case of an error, an additional error message is returned.
PUT /projects/:id/labels
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
-| `name` | string | yes | The name of the existing label |
-| `new_name` | string | yes if `color` if not provided | The new name of the label |
-| `color` | string | yes if `new_name` is not provided | The new color of the label in 6-digit hex notation with leading `#` sign |
+| Attribute | Type | Required | Description |
+| --------------- | ------- | --------------------------------- | ------------------------------- |
+| `id` | integer | yes | The ID of the project |
+| `name` | string | yes | The name of the existing label |
+| `new_name` | string | yes if `color` if not provided | The new name of the label |
+| `color` | string | yes if `new_name` is not provided | The new color of the label in 6-digit hex notation with leading `#` sign |
+| `description` | string | no | The new description of the label |
```bash
-curl -X PUT --data "name=documentation&new_name=docs&color=#8E44AD" -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/labels"
+curl -X PUT --data "name=documentation&new_name=docs&color=#8E44AD&description=Documentation" -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/labels"
```
Example response:
@@ -149,6 +161,7 @@ Example response:
```json
{
"color" : "#8E44AD",
- "name" : "docs"
+ "name" : "docs",
+ "description": "Documentation"
}
```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 3703f4b327a..3a909a2bc87 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -491,6 +491,172 @@ Parameters:
- `id` (required) - The ID of the project to be forked
+### Archive a project
+
+Archives the project if the user is either admin or the project owner of this project. This action is
+idempotent, thus archiving an already archived project will not change the project.
+
+Status code 201 with the project as body is given when successful, in case the user doesn't
+have the proper access rights, code 403 is returned. Status 404 is returned if the project
+doesn't exist, or is hidden to the user.
+
+```
+POST /projects/:id/archive
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of the project |
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/archive"
+```
+
+Example response:
+
+```json
+{
+ "id": 3,
+ "description": null,
+ "default_branch": "master",
+ "public": false,
+ "visibility_level": 0,
+ "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
+ "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
+ "web_url": "http://example.com/diaspora/diaspora-project-site",
+ "tag_list": [
+ "example",
+ "disapora project"
+ ],
+ "owner": {
+ "id": 3,
+ "name": "Diaspora",
+ "created_at": "2013-09-30T13: 46: 02Z"
+ },
+ "name": "Diaspora Project Site",
+ "name_with_namespace": "Diaspora / Diaspora Project Site",
+ "path": "diaspora-project-site",
+ "path_with_namespace": "diaspora/diaspora-project-site",
+ "issues_enabled": true,
+ "open_issues_count": 1,
+ "merge_requests_enabled": true,
+ "builds_enabled": true,
+ "wiki_enabled": true,
+ "snippets_enabled": false,
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "last_activity_at": "2013-09-30T13: 46: 02Z",
+ "creator_id": 3,
+ "namespace": {
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "description": "",
+ "id": 3,
+ "name": "Diaspora",
+ "owner_id": 1,
+ "path": "diaspora",
+ "updated_at": "2013-09-30T13: 46: 02Z"
+ },
+ "permissions": {
+ "project_access": {
+ "access_level": 10,
+ "notification_level": 3
+ },
+ "group_access": {
+ "access_level": 50,
+ "notification_level": 3
+ }
+ },
+ "archived": true,
+ "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+ "shared_runners_enabled": true,
+ "forks_count": 0,
+ "star_count": 0,
+ "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b"
+}
+```
+
+### Unarchive a project
+
+Unarchives the project if the user is either admin or the project owner of this project. This action is
+idempotent, thus unarchiving an non-archived project will not change the project.
+
+Status code 201 with the project as body is given when successful, in case the user doesn't
+have the proper access rights, code 403 is returned. Status 404 is returned if the project
+doesn't exist, or is hidden to the user.
+
+```
+POST /projects/:id/archive
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of the project |
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/unarchive"
+```
+
+Example response:
+
+```json
+{
+ "id": 3,
+ "description": null,
+ "default_branch": "master",
+ "public": false,
+ "visibility_level": 0,
+ "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
+ "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
+ "web_url": "http://example.com/diaspora/diaspora-project-site",
+ "tag_list": [
+ "example",
+ "disapora project"
+ ],
+ "owner": {
+ "id": 3,
+ "name": "Diaspora",
+ "created_at": "2013-09-30T13: 46: 02Z"
+ },
+ "name": "Diaspora Project Site",
+ "name_with_namespace": "Diaspora / Diaspora Project Site",
+ "path": "diaspora-project-site",
+ "path_with_namespace": "diaspora/diaspora-project-site",
+ "issues_enabled": true,
+ "open_issues_count": 1,
+ "merge_requests_enabled": true,
+ "builds_enabled": true,
+ "wiki_enabled": true,
+ "snippets_enabled": false,
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "last_activity_at": "2013-09-30T13: 46: 02Z",
+ "creator_id": 3,
+ "namespace": {
+ "created_at": "2013-09-30T13: 46: 02Z",
+ "description": "",
+ "id": 3,
+ "name": "Diaspora",
+ "owner_id": 1,
+ "path": "diaspora",
+ "updated_at": "2013-09-30T13: 46: 02Z"
+ },
+ "permissions": {
+ "project_access": {
+ "access_level": 10,
+ "notification_level": 3
+ },
+ "group_access": {
+ "access_level": 50,
+ "notification_level": 3
+ }
+ },
+ "archived": false,
+ "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+ "shared_runners_enabled": true,
+ "forks_count": 0,
+ "star_count": 0,
+ "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b"
+}
+```
+
### Remove project
Removes a project including all associated resources (issues, merge requests etc.)
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 001de76c7af..1e745115dc8 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -26,7 +26,6 @@ Example response:
"default_branch_protection" : 2,
"restricted_visibility_levels" : [],
"signin_enabled" : true,
- "twitter_sharing_enabled" : true,
"after_sign_out_path" : null,
"max_attachment_size" : 10,
"user_oauth_applications" : true,
@@ -57,7 +56,6 @@ PUT /application/settings
| `sign_in_text` | string | no | Text on login page |
| `home_page_url` | string | no | Redirect to this URL when not logged in |
| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `1`. |
-| `twitter_sharing_enabled` | boolean | no | Allow users to share project creation on Twitter |
| `restricted_visibility_levels` | array of integers | no | Selected levels cannot be used by non-admin users for projects or snippets. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is null which means there is no restriction. |
| `max_attachment_size` | integer | no | Limit attachment size in MB |
| `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes |
@@ -85,7 +83,6 @@ Example response:
"updated_at": "2015-06-30T13:22:42.210Z",
"home_page_url": "",
"default_branch_protection": 2,
- "twitter_sharing_enabled": true,
"restricted_visibility_levels": [],
"max_attachment_size": 10,
"session_expire_delay": 10080,
diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md
index 4cfb8402943..5a9a1582877 100644
--- a/doc/incoming_email/README.md
+++ b/doc/incoming_email/README.md
@@ -1,36 +1,99 @@
# Reply by email
-GitLab can be set up to allow users to comment on issues and merge requests by replying to notification emails.
+GitLab can be set up to allow users to comment on issues and merge requests by
+replying to notification emails.
-## Get a mailbox
+## Requirement
-Reply by email requires an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Google Apps, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix mail server which you can run on-premises.
+Reply by email requires an IMAP-enabled email account. GitLab allows you to use
+three strategies for this feature:
+- using email sub-addressing
+- using a dedicated email address
+- using a catch-all mailbox
-If you want to use Gmail / Google Apps with Reply by email, make sure you have [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) and [allow less secure apps to access the account](https://support.google.com/accounts/answer/6010255).
+### Email sub-addressing
-To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these instructions](./postfix.md).
+**If your provider or server supports email sub-addressing, we recommend using it.**
+
+[Sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing) is
+a feature where any email to `user+some_arbitrary_tag@example.com` will end up
+in the mailbox for `user@example.com`, and is supported by providers such as
+Gmail, Google Apps, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix
+mail server which you can run on-premises.
+
+### Dedicated email address
+
+This solution is really simple to set up: you just have to create an email
+address dedicated to receive your users' replies to GitLab notifications.
+
+### Catch-all mailbox
+
+A [catch-all mailbox](https://en.wikipedia.org/wiki/Catch-all) for a domain will
+"catch all" the emails addressed to the domain that do not exist in the mail
+server.
+
+## How it works?
+
+### 1. GitLab sends a notification email
+
+When GitLab sends a notification and Reply by email is enabled, the `Reply-To`
+header is set to the address defined in your GitLab configuration, with the
+`%{key}` placeholder (if present) replaced by a specific "reply key". In
+addition, this "reply key" is also added to the `References` header.
+
+### 2. You reply to the notification email
+
+When you reply to the notification email, your email client will:
+
+- send the email to the `Reply-To` address it got from the notification email
+- set the `In-Reply-To` header to the value of the `Message-ID` header from the
+ notification email
+- set the `References` header to the value of the `Message-ID` plus the value of
+ the notification email's `References` header.
+
+### 3. GitLab receives your reply to the notification email
+
+When GitLab receives your reply, it will look for the "reply key" in the
+following headers, in this order:
+
+1. the `To` header
+1. the `References` header
+
+If it finds a reply key, it will be able to leave your reply as a comment on
+the entity the notification was about (issue, merge request, commit...).
+
+For more details about the `Message-ID`, `In-Reply-To`, and `References headers`,
+please consult [RFC 5322](https://tools.ietf.org/html/rfc5322#section-3.6.4).
## Set it up
+If you want to use Gmail / Google Apps with Reply by email, make sure you have
+[IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018)
+and [allowed less secure apps to access the account](https://support.google.com/accounts/answer/6010255).
+
+To set up a basic Postfix mail server with IMAP access on Ubuntu, follow
+[these instructions](./postfix.md).
+
### Omnibus package installations
-1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature and fill in the details for your specific IMAP server and email account:
+1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the
+ feature and fill in the details for your specific IMAP server and email account:
```ruby
# Configuration for Postfix mail server, assumes mailbox incoming@gitlab.example.com
gitlab_rails['incoming_email_enabled'] = true
-
- # The email address including a placeholder for the key that references the item being replied to.
- # The `%{key}` placeholder is added after the user part, before the `@`.
+
+ # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
-
+
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
gitlab_rails['incoming_email_email'] = "incoming"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
-
+
# IMAP server host
gitlab_rails['incoming_email_host'] = "gitlab.example.com"
# IMAP server port
@@ -47,18 +110,18 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
```ruby
# Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
gitlab_rails['incoming_email_enabled'] = true
-
+
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The `%{key}` placeholder is added after the user part, after a `+` character, before the `@`.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
-
+
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"
# Email account password
gitlab_rails['incoming_email_password'] = "[REDACTED]"
-
+
# IMAP server host
gitlab_rails['incoming_email_host'] = "imap.gmail.com"
# IMAP server port
@@ -72,8 +135,6 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
gitlab_rails['incoming_email_mailbox_name'] = "inbox"
```
- As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`.
-
1. Reconfigure GitLab and restart mailroom for the changes to take effect:
```sh
@@ -97,7 +158,8 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
cd /home/git/gitlab
```
-1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and fill in the details for your specific IMAP server and email account:
+1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature
+ and fill in the details for your specific IMAP server and email account:
```sh
sudo editor config/gitlab.yml
@@ -109,7 +171,7 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The `%{key}` placeholder is added after the user part, after a `+` character, before the `@`.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "incoming+%{key}@gitlab.example.com"
# Email account username
@@ -138,7 +200,7 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The `%{key}` placeholder is added after the user part, after a `+` character, before the `@`.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
@@ -161,8 +223,6 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
mailbox: "inbox"
```
- As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`.
-
1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
```sh
@@ -195,8 +255,8 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
incoming_email:
enabled: true
- # The email address including a placeholder for the key that references the item being replied to.
- # The `%{key}` placeholder is added after the user part, before the `@`.
+ # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
diff --git a/doc/install/installation.md b/doc/install/installation.md
index bffbc776500..e0a16df09c1 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -227,9 +227,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-6-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-7-stable gitlab
-**Note:** You can change `8-6-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `8-7-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
diff --git a/doc/update/8.6-to-8.7.md b/doc/update/8.6-to-8.7.md
new file mode 100644
index 00000000000..76eee147c72
--- /dev/null
+++ b/doc/update/8.6-to-8.7.md
@@ -0,0 +1,146 @@
+# From 8.6 to 8.7
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-7-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-7-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout v2.7.0
+```
+
+### 5. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires
+[Go 1.5](https://golang.org/dl) which should already be on your system from
+GitLab 8.1.
+
+```bash
+cd /home/git/gitlab-workhorse
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout v0.7.1
+sudo -u git -H make
+```
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+```
+
+### 7. Update configuration files
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+# For HTTPS configurations
+git diff origin/8-6-stable:lib/support/nginx/gitlab-ssl origin/8-7-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/8-6-stable:lib/support/nginx/gitlab origin/8-7-stable:lib/support/nginx/gitlab
+```
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-7-stable/lib/support/init.d/gitlab.default.example#L37
+
+#### Init script
+
+Ensure you're still up-to-date with the latest init script changes:
+
+ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+
+### 8. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 9. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.6)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.5 to 8.6](8.5-to-8.6.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature
index c3b3577c449..db73309804c 100644
--- a/features/dashboard/dashboard.feature
+++ b/features/dashboard/dashboard.feature
@@ -6,6 +6,7 @@ Feature: Dashboard
And project "Shop" has push event
And project "Shop" has CI enabled
And project "Shop" has CI build
+ And project "Shop" has labels: "bug", "feature", "enhancement"
And I visit dashboard page
Scenario: I should see projects list
@@ -51,6 +52,13 @@ Feature: Dashboard
Then The list should be sorted by "Oldest updated"
@javascript
+ Scenario: Filtering Issues by label
+ Given project "Shop" has issue "Bugfix1" with label "feature"
+ When I visit dashboard issues page
+ And I filter the list by label "feature"
+ Then I should see "Bugfix1" in issues list
+
+ @javascript
Scenario: Visiting Project's issues after sorting
Given I visit dashboard issues page
And I sort the list by "Oldest updated"
diff --git a/features/dashboard/todos.feature b/features/dashboard/todos.feature
index 1e7b1b50d64..8677b450813 100644
--- a/features/dashboard/todos.feature
+++ b/features/dashboard/todos.feature
@@ -36,3 +36,8 @@ Feature: Dashboard Todos
Scenario: I filter by action
Given I filter by "Mentioned"
Then I should not see todos related to "Assignments" in the list
+
+ @javascript
+ Scenario: I click on a todo row
+ Given I click on the todo
+ Then I should be directed to the corresponding page
diff --git a/features/groups.feature b/features/groups.feature
index 419a5d3963d..49e939807b5 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -7,10 +7,6 @@ Feature: Groups
When I visit group "NonExistentGroup" page
Then page status code should be 404
- Scenario: I should have back to group button
- When I visit group "Owned" page
- Then I should see back to dashboard button
-
@javascript
Scenario: I should see group "Owned" dashboard list
When I visit group "Owned" page
diff --git a/features/project/project.feature b/features/project/project.feature
index f1f3ed26065..aa22401c88e 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -18,15 +18,6 @@ Feature: Project
Then I should see the default project avatar
And I should not see the "Remove avatar" button
- Scenario: I should have back to group button
- And project "Shop" belongs to group
- And I visit project "Shop" page
- Then I should see back to group button
-
- Scenario: I should have back to group button
- And I visit project "Shop" page
- Then I should see back to dashboard button
-
Scenario: I should have readme on page
And I visit project "Shop" page
Then I should see project "Shop" README
diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb
index 5062e348844..b5980b35102 100644
--- a/features/steps/dashboard/dashboard.rb
+++ b/features/steps/dashboard/dashboard.rb
@@ -87,4 +87,23 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps
step 'I should see 1 project at group list' do
expect(find('span.last_activity/span')).to have_content('1')
end
+
+ step 'I filter the list by label "feature"' do
+ page.within ".labels-filter" do
+ find('.dropdown').click
+ click_link "feature"
+ end
+ end
+
+ step 'I should see "Bugfix1" in issues list' do
+ page.within "ul.content-list" do
+ expect(page).to have_content "Bugfix1"
+ end
+ end
+
+ step 'project "Shop" has issue "Bugfix1" with label "feature"' do
+ project = Project.find_by(name: "Shop")
+ issue = create(:issue, title: "Bugfix1", project: project, assignee: current_user)
+ issue.labels << project.labels.find_by(title: 'feature')
+ end
end
diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb
index 93aa77589be..e21af72a777 100644
--- a/features/steps/dashboard/issues.rb
+++ b/features/steps/dashboard/issues.rb
@@ -42,11 +42,10 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
end
step 'I click "All" link' do
- find('.js-author-search').click
- find('.dropdown-content a', match: :first).click
-
- find('.js-assignee-search').click
- find('.dropdown-content a', match: :first).click
+ find(".js-author-search").click
+ find(".dropdown-menu-author li a", match: :first).click
+ find(".js-assignee-search").click
+ find(".dropdown-menu-assignee li a", match: :first).click
end
def should_see(issue)
diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb
index 963e4f21365..30b21b93ac7 100644
--- a/features/steps/dashboard/todos.rb
+++ b/features/steps/dashboard/todos.rb
@@ -88,6 +88,14 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
should_not_see_todo "John Doe assigned you issue ##{issue.iid}"
end
+ step 'I click on the todo' do
+ find('.todo:nth-child(1)').click
+ end
+
+ step 'I should be directed to the corresponding page' do
+ page.should have_css('.identifier', text: 'Merge Request !1')
+ end
+
def should_see_todo(position, title, body, pending = true)
page.within(".todo:nth-child(#{position})") do
expect(page).to have_content title
diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb
index a167d259837..b6ce5bc9cec 100644
--- a/features/steps/group/milestones.rb
+++ b/features/steps/group/milestones.rb
@@ -5,7 +5,9 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
include SharedUser
step 'I click on group milestones' do
- click_link 'Milestones'
+ page.within '.nav-secondary' do
+ click_link("Milestones")
+ end
end
step 'I should see group milestones index page has no milestones' do
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index e5b7db4c5e3..483370f41c6 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -4,10 +4,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
include SharedGroup
include SharedUser
- step 'I should see back to dashboard button' do
- expect(page).to have_content 'Go to dashboard'
- end
-
step 'I should see group "Owned"' do
expect(page).to have_content '@owned'
end
diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb
index 19d81453d8c..4584fc4d754 100644
--- a/features/steps/project/active_tab.rb
+++ b/features/steps/project/active_tab.rb
@@ -82,7 +82,9 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
# Sub Tabs: Issues
step 'I click the "Milestones" tab' do
- click_link('Milestones')
+ page.within '.nav-secondary' do
+ click_link('Milestones')
+ end
end
step 'I click the "Labels" tab' do
diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb
index 527f7853da9..d9b16afa9b8 100644
--- a/features/steps/project/fork.rb
+++ b/features/steps/project/fork.rb
@@ -36,7 +36,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
end
step 'I goto the Merge Requests page' do
- page.within '.page-sidebar-expanded' do
+ page.within '.nav-secondary' do
click_link "Merge Requests"
end
end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 91fe19dd477..a4f02b590ea 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -326,7 +326,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see a discussion has started on diff' do
page.within(".notes .discussion") do
- page.should have_content "#{current_user.name} started a discussion"
+ page.should have_content "#{current_user.name} #{current_user.to_reference} started a discussion"
page.should have_content sample_commit.line_code_path
page.should have_content "Line is wrong"
end
@@ -334,7 +334,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see a discussion by user "John Doe" has started on diff' do
page.within(".notes .discussion") do
- page.should have_content "#{user_exists("John Doe").name} started a discussion"
+ page.should have_content "#{user_exists("John Doe").name} #{user_exists("John Doe").to_reference} started a discussion"
page.should have_content sample_commit.line_code_path
page.should have_content "Line is wrong"
end
@@ -350,7 +350,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see a discussion has started on commit diff' do
page.within(".notes .discussion") do
- page.should have_content "#{current_user.name} started a discussion on commit"
+ page.should have_content "#{current_user.name} #{current_user.to_reference} started a discussion on commit"
page.should have_content sample_commit.line_code_path
page.should have_content "Line is wrong"
end
@@ -358,7 +358,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see a discussion has started on commit' do
page.within(".notes .discussion") do
- page.should have_content "#{current_user.name} started a discussion on commit"
+ page.should have_content "#{current_user.name} #{current_user.to_reference} started a discussion on commit"
page.should have_content "One comment to rule them all"
end
end
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index ef185861e00..8f1d4a223a9 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -114,7 +114,9 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should not see "Snippets" button' do
- expect(page).not_to have_link 'Snippets'
+ page.within '.nav-secondary' do
+ expect(page).not_to have_link 'Snippets'
+ end
end
step 'project "Shop" belongs to group' do
@@ -123,14 +125,6 @@ class Spinach::Features::Project < Spinach::FeatureSteps
@project.save!
end
- step 'I should see back to dashboard button' do
- expect(page).to have_content 'Go to dashboard'
- end
-
- step 'I should see back to group button' do
- expect(page).to have_content 'Go to group'
- end
-
step 'I click notifications drop down button' do
click_link 'notifications-button'
end
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index 223b7277b51..9f6aed1c5b9 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -85,7 +85,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
end
step 'I have an existing Wiki page with images linked on page' do
- wiki.create_page("pictures", "Look at this [image](image.jpg)\n\n ![image](image.jpg)", :markdown, "first commit")
+ wiki.create_page("pictures", "Look at this [image](image.jpg)\n\n ![alt text](image.jpg)", :markdown, "first commit")
@wiki_page = wiki.find_page("pictures")
end
diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb
index 4fc2ece79ff..fa7d24ce611 100644
--- a/features/steps/shared/project_tab.rb
+++ b/features/steps/shared/project_tab.rb
@@ -41,7 +41,7 @@ module SharedProjectTab
end
step 'the active main tab should be Settings' do
- page.within '.nav-sidebar' do
+ page.within '.nav-secondary' do
expect(page).to have_content('Go to project')
end
end
diff --git a/fixtures/emojis/digests.json b/fixtures/emojis/digests.json
new file mode 100644
index 00000000000..18d6e93e0f4
--- /dev/null
+++ b/fixtures/emojis/digests.json
@@ -0,0 +1,8597 @@
+[
+ {
+ "name": "100",
+ "unicode": "1F4AF",
+ "digest": "6d57c7cc93335f853e1a5670233f121bc94730dbd82b2b3c5c5a509e092ef0fd"
+ },
+ {
+ "name": "1234",
+ "unicode": "1F522",
+ "digest": "727763fd9f18fd5df59e9f78e678ea4ec753e674d70f15d4e77c7802067d660b"
+ },
+ {
+ "name": "8ball",
+ "unicode": "1F3B1",
+ "digest": "1aecf21951452ba24e921ec71b3d313b7ddc2e185b0339c9e0eebc85be4f031d"
+ },
+ {
+ "name": "a",
+ "unicode": "1F170",
+ "digest": "2272113a5bcb7faf8db7c1bd35df576d32f2f7cbd881463934ad3382eb87c723"
+ },
+ {
+ "name": "ab",
+ "unicode": "1F18E",
+ "digest": "6f8a237751fdc84db4121f408272d9a23258515449610e4c6c54f50f6e995627"
+ },
+ {
+ "name": "abc",
+ "unicode": "1F524",
+ "digest": "652a2381a7b587d8a52d5178e2d7d6c8600b33d36160fa69677943da374105bc"
+ },
+ {
+ "name": "abcd",
+ "unicode": "1F521",
+ "digest": "35ade4fd3d75294ebb72c24490aa32745604edc6cabe095b90634cd3ce78c07b"
+ },
+ {
+ "name": "accept",
+ "unicode": "1F251",
+ "digest": "8212ed158cc447c92813273fc915e84d3d5c4c48d1b38e498c088bad27ab8145"
+ },
+ {
+ "name": "aerial_tramway",
+ "unicode": "1F6A1",
+ "digest": "8039d7f67e6e5b211066cab6cf2142afc3aca5c830a357369362c9b484029563"
+ },
+ {
+ "name": "airplane",
+ "unicode": "2708",
+ "digest": "18f4dfac323555d8cdabb79148874c0185ce98e1a08e69414d236b23e502a854"
+ },
+ {
+ "name": "airplane_arriving",
+ "unicode": "1F6EC",
+ "digest": "9a1c81d97512e5d0e3acec40290d00f616ec182140909859e366a734b9f840bb"
+ },
+ {
+ "name": "airplane_departure",
+ "unicode": "1F6EB",
+ "digest": "e3c5ff4038db998c1897cb237d0b865da0bc60331c758f204e45a979d5fab445"
+ },
+ {
+ "name": "airplane_northeast",
+ "unicode": "1F6EA",
+ "digest": "fdddc2cd3618ec6661612581b8b93553cb086b0bb197e96aedf1bee8055e7bb4"
+ },
+ {
+ "name": "airplane_small",
+ "unicode": "1F6E9",
+ "digest": "f98b44422d6bf505b50330805ecf68013d035341f0b6487c3c05ad913eb5abd3"
+ },
+ {
+ "name": "airplane_small_up",
+ "unicode": "1F6E8",
+ "digest": "029752b29a757c087dec60f45ea242e974fc181129e20390d5d4a2f90442091a"
+ },
+ {
+ "name": "airplane_up",
+ "unicode": "1F6E7",
+ "digest": "ec45d4dbfce1f75dc59339417b1dcf5f1e1359cd9d04ff233babf359a3330e77"
+ },
+ {
+ "name": "alarm_clock",
+ "unicode": "23F0",
+ "digest": "84ddd7b3b857c165410b7b44863e5354ca0f3591c3bfe56231f12c9f7531a96f"
+ },
+ {
+ "name": "alembic",
+ "unicode": "2697",
+ "digest": "45698914a21683f06931d807af171bcb6984e5ebce66012bba71b467565bd69d"
+ },
+ {
+ "name": "alien",
+ "unicode": "1F47D",
+ "digest": "94dbe4e90614c654145aba93610c43e3ab86df8ca07391bd4e56383f9329c008"
+ },
+ {
+ "name": "ambulance",
+ "unicode": "1F691",
+ "digest": "82ef36bcd13c88a4b2397c918b8048adc6bf045ed2532ff568e0dfd1b1b29c3c"
+ },
+ {
+ "name": "amphora",
+ "unicode": "1F3FA",
+ "digest": "d3758d88aa1fc3be01894102f57479d3a49790510d38ad3d06a2774962010608"
+ },
+ {
+ "name": "anchor",
+ "unicode": "2693",
+ "digest": "27c6034f769d9f020362fc5b227b9279651cc940861e727d1f6ccd59af98f851"
+ },
+ {
+ "name": "angel",
+ "unicode": "1F47C",
+ "digest": "c1b8ad2adc7686e7fbbe4ec357071e7228a5e0762e001bb589e2f97ff258d5c7"
+ },
+ {
+ "name": "angel_tone1",
+ "unicode": "1F47C-1F3FB",
+ "digest": "90b701c43311b1096c4a012d9905a186f1a16829ea2707921a8418c28617d751"
+ },
+ {
+ "name": "angel_tone2",
+ "unicode": "1F47C-1F3FC",
+ "digest": "d6bcaf1b76e25d486d4ab9b159cf727782d508543d1ae27c8d2c12d2f13d6eb0"
+ },
+ {
+ "name": "angel_tone3",
+ "unicode": "1F47C-1F3FD",
+ "digest": "3069285e6218c8083cb0085aa10017bcdea033e321d97ba339a84892074b903a"
+ },
+ {
+ "name": "angel_tone4",
+ "unicode": "1F47C-1F3FE",
+ "digest": "dbb87019752d9caa94ce086858c1e3225b62e221ad599f5106548fda2456fc2b"
+ },
+ {
+ "name": "angel_tone5",
+ "unicode": "1F47C-1F3FF",
+ "digest": "f77703df97720c27a128b5f3c0948b9e04a6b6b81ea5306468154f9bf56225db"
+ },
+ {
+ "name": "anger",
+ "unicode": "1F4A2",
+ "digest": "2253b7ff0894f247bc6f04d841a748c56d6c94684880c13df42387691ff20e75"
+ },
+ {
+ "name": "anger_left",
+ "unicode": "1F5EE",
+ "digest": "f2711991e8b386b2d5b12f296ce20a9b4b00ef91d6d67af2cf4e06abf2faa1dc"
+ },
+ {
+ "name": "anger_right",
+ "unicode": "1F5EF",
+ "digest": "24b572d64c519251a3ae8844e8d66fd6955752aff99aebe7dc20179505a466c4"
+ },
+ {
+ "name": "angry",
+ "unicode": "1F620",
+ "digest": "c4188ba70df99d8ccef5706d711176725d3dd50d62f065a177d68d85c7828107"
+ },
+ {
+ "name": "anguished",
+ "unicode": "1F627",
+ "digest": "9c2347308133ae50dc04da62042fff847f4c477b2956b8aa976f0413899e38bc"
+ },
+ {
+ "name": "ant",
+ "unicode": "1F41C",
+ "digest": "d2af2ed1cfe15d649aa329d965764a1e8726941d833841781a5b66d7dd0b0921"
+ },
+ {
+ "name": "apple",
+ "unicode": "1F34E",
+ "digest": "a9babee24f454934a5e1fb8d781cbce354dfd88e8a8e01f02e8b30071fd40460"
+ },
+ {
+ "name": "aquarius",
+ "unicode": "2652",
+ "digest": "1a168c252678847d1f9ef450887489e3bdc207ecae4b6fb05e92295ff861ae2c"
+ },
+ {
+ "name": "aries",
+ "unicode": "2648",
+ "digest": "bde262a8795e12f8b0ebb3f0f8c3a56104062fcee8d5d678cf4bb445a7daf698"
+ },
+ {
+ "name": "arrow_backward",
+ "unicode": "25C0",
+ "digest": "ddae36d1febf5c246e51d599e2898a8aa30cd47f88b5bcb469e3ca9d22538b97"
+ },
+ {
+ "name": "arrow_double_down",
+ "unicode": "23EC",
+ "digest": "906f42b5f788128ed90d2d162cf03e6e595a50ad05e0aa5f64e925637379d0cd"
+ },
+ {
+ "name": "arrow_double_up",
+ "unicode": "23EB",
+ "digest": "2129a57402980de6fc6f59ad8354525c2dbcd66d1b78f4de091181ddc81e0693"
+ },
+ {
+ "name": "arrow_down",
+ "unicode": "2B07",
+ "digest": "370e4f41565d5dab245c20e45c502505a56d26c2392283781b841eb3e905edb2"
+ },
+ {
+ "name": "arrow_down_small",
+ "unicode": "1F53D",
+ "digest": "98a2b183f2daec425160bbfce1d2b940b8baa0d5032fdacfa9453e39bed5651b"
+ },
+ {
+ "name": "arrow_forward",
+ "unicode": "25B6",
+ "digest": "348627b8e0f55cf1e9ab19c9de1d170371b2c4cb4dda9a2aa8e0c558db08b18a"
+ },
+ {
+ "name": "arrow_heading_down",
+ "unicode": "2935",
+ "digest": "96c64953fc3134711247bef320f252c48993ebc90494925b7fee42ffce2a2ec2"
+ },
+ {
+ "name": "arrow_heading_up",
+ "unicode": "2934",
+ "digest": "94f94e74176cc050703b3584f3f700debf86e4e61b893a441825a21fa3f8ce74"
+ },
+ {
+ "name": "arrow_left",
+ "unicode": "2B05",
+ "digest": "4553be62a63d7550deac4f7dbeffce6006f769ae6cddfb8c795671672011ba0b"
+ },
+ {
+ "name": "arrow_lower_left",
+ "unicode": "2199",
+ "digest": "10f83c252110d705cdcfebc35a70c341ad288730d0c0729479e3a96e263d5120"
+ },
+ {
+ "name": "arrow_lower_right",
+ "unicode": "2198",
+ "digest": "ee33abd4c96c19e9b80a2fc1500ba8ecaa6668c49310cc816a496e8c61af3850"
+ },
+ {
+ "name": "arrow_right",
+ "unicode": "27A1",
+ "digest": "2611e9138a2651916f414015d0287f5f0af266514d96a42915d32b04fb652a90"
+ },
+ {
+ "name": "arrow_right_hook",
+ "unicode": "21AA",
+ "digest": "628b06384a2963a4fe81e9fbf4e22511f697878d9b9db7d2fc98f8aadbe8f4f9"
+ },
+ {
+ "name": "arrow_up",
+ "unicode": "2B06",
+ "digest": "c09e5f41c01028b45707c525d30d3d6731ec57b7447f0d7ba4ad6c1404449e5c"
+ },
+ {
+ "name": "arrow_up_down",
+ "unicode": "2195",
+ "digest": "e7fd92d24a01702f76c7fcc0de998bc81fbfb93711d076984f6da91d1dccd84c"
+ },
+ {
+ "name": "arrow_up_small",
+ "unicode": "1F53C",
+ "digest": "bc48dad74bc1d0c5579cbf5e3d005314b0d21bc5b5ebbba2b05136e33f49296d"
+ },
+ {
+ "name": "arrow_upper_left",
+ "unicode": "2196",
+ "digest": "792a9709f03843024e53d201cb4769c59b656c3bf0dff2306e8e605493a66b93"
+ },
+ {
+ "name": "arrow_upper_right",
+ "unicode": "2197",
+ "digest": "ee934b0c9cff270efd30a6cafc15253d405efd2c93b4785ac2ed4ea6420266a6"
+ },
+ {
+ "name": "arrows_clockwise",
+ "unicode": "1F503",
+ "digest": "914f4120513730d7a19c9f8c4e59223a90568de0b25a225b712b31fa9697ef4f"
+ },
+ {
+ "name": "arrows_counterclockwise",
+ "unicode": "1F504",
+ "digest": "86d87597e4e3db6dbba9907ee82412db0cbab1ea875bd0be6505dd886dc19b90"
+ },
+ {
+ "name": "art",
+ "unicode": "1F3A8",
+ "digest": "dfc6b0da780199df86507d65b0499ba1706c266ae7badcb0e7fb5b85af7c9578"
+ },
+ {
+ "name": "articulated_lorry",
+ "unicode": "1F69B",
+ "digest": "4c4de240ebd175f7b53453eda4e51f2e57d0db2a98d317f804116e14e47cff1d"
+ },
+ {
+ "name": "ascending_notes",
+ "unicode": "1F39C",
+ "digest": "33432042771d456338dda5d98e49322d3600f2cc9049963480c7c38d9de1ef0a"
+ },
+ {
+ "name": "asterisk",
+ "unicode": "002A-20E3",
+ "digest": "0b7f27f545b616677c83d40ff957337477b2881459b4d3c839ae55e23797419f"
+ },
+ {
+ "name": "astonished",
+ "unicode": "1F632",
+ "digest": "58632b97e274ade5183752db2b3c5c4fe29effcd5a9720a8d01fa809b97023dc"
+ },
+ {
+ "name": "athletic_shoe",
+ "unicode": "1F45F",
+ "digest": "1fc55d85a4d6751f9e60467801b051d2fb3341bdcc33b8d3695d5143359edb43"
+ },
+ {
+ "name": "atm",
+ "unicode": "1F3E7",
+ "digest": "bf827ef6c349f5b6912d821457975a4720d1750529d907e94ece429b7a388d7e"
+ },
+ {
+ "name": "atom",
+ "unicode": "269B",
+ "digest": "cbce1725602efbb77a935cfae5407e4d75489ee988910296c7f6140665afc669"
+ },
+ {
+ "name": "b",
+ "unicode": "1F171",
+ "digest": "9116256b3189977e37f6da7ddedf82bb29b0358829a4e8718fd59e51d9b86b3c"
+ },
+ {
+ "name": "baby",
+ "unicode": "1F476",
+ "digest": "66596bea11015154e0b1752b85f349f4286c6643ee6f51ee5e60e0d625c4ae9a"
+ },
+ {
+ "name": "baby_bottle",
+ "unicode": "1F37C",
+ "digest": "ed42994b4a539b8bfeccde0f3c7e9c7f54d6696ff48ce7e48171bbab51002348"
+ },
+ {
+ "name": "baby_chick",
+ "unicode": "1F424",
+ "digest": "ea2cfa0e5c2cbff5fffdb52cc04dfe7872834bd7cfeaa45e0541b8faffcbd0e9"
+ },
+ {
+ "name": "baby_symbol",
+ "unicode": "1F6BC",
+ "digest": "65df04dff8739b86f7663ae9c0648927341f360a986655e109721b0e16013b75"
+ },
+ {
+ "name": "baby_tone1",
+ "unicode": "1F476-1F3FB",
+ "digest": "bc747527a2d723cf99ef3fc2539c19d29634c92ff417736982d3bf87d65d06eb"
+ },
+ {
+ "name": "baby_tone2",
+ "unicode": "1F476-1F3FC",
+ "digest": "b82bba7a666b7d070751726e54acc7fb8f96e2dfc09e9610d61cfd20947aef9c"
+ },
+ {
+ "name": "baby_tone3",
+ "unicode": "1F476-1F3FD",
+ "digest": "7f45dfd4ea2ae8515d419ffa13e7ee5c625b024b4e521ace5344c414bb929da0"
+ },
+ {
+ "name": "baby_tone4",
+ "unicode": "1F476-1F3FE",
+ "digest": "80b1854626616f15426649cc6415e4911a55c8f761422fe48a08af9e8ac6a7cb"
+ },
+ {
+ "name": "baby_tone5",
+ "unicode": "1F476-1F3FF",
+ "digest": "9f890804d19a61bee76a29644c818045dd96cf69d67cfbca2d11f4ad376b27da"
+ },
+ {
+ "name": "back",
+ "unicode": "1F519",
+ "digest": "1dc73947b8f56e033777ca3f747407923bd16b07e53a6c78b09950ca474b7e7a"
+ },
+ {
+ "name": "badminton",
+ "unicode": "1F3F8",
+ "digest": "3f95180c1175d0248ebf4b8650cf86566c39e0486d828078244080194c14d4fe"
+ },
+ {
+ "name": "baggage_claim",
+ "unicode": "1F6C4",
+ "digest": "7c1a69511aa2a93984d601da4d1cef1cb4cefbbf127b1486278da8c01345bbf3"
+ },
+ {
+ "name": "balloon",
+ "unicode": "1F388",
+ "digest": "a10c2b0865179cdbdef339494ec9b2a109451a356e53738d6a9dd43232500956"
+ },
+ {
+ "name": "ballot_box",
+ "unicode": "1F5F3",
+ "digest": "0455ea75612efe78354315b4c345953d2d559bb471d5b01c1adc1d6b74ed693a"
+ },
+ {
+ "name": "ballot_box_check",
+ "unicode": "1F5F9",
+ "digest": "fc3ba16c009d963a4a0ea20a348ac98eee3c4c18c481df19a5ada0d1de7fcc15"
+ },
+ {
+ "name": "ballot_box_with_check",
+ "unicode": "2611",
+ "digest": "5f5cec7fe462557d31e8d2b836534c1e76d546cc0061236fa2af3667972b84aa"
+ },
+ {
+ "name": "ballot_box_x",
+ "unicode": "1F5F5",
+ "digest": "861dcfc2361298262587b5d0e163fed96a55c44636361f5b4a9ab1d6502b8928"
+ },
+ {
+ "name": "ballot_x",
+ "unicode": "1F5F4",
+ "digest": "0b73b89847eb82bcad5664644c8af237e0aef6c3d8c94b7a5df94e05d0ebf4e1"
+ },
+ {
+ "name": "bamboo",
+ "unicode": "1F38D",
+ "digest": "feb0cf2f1012a1c0649b8c66f7e96e2d8bcdefe879c5a52dab3e25c51009e3b2"
+ },
+ {
+ "name": "banana",
+ "unicode": "1F34C",
+ "digest": "aa9a1e6db00efa94a7f414c570eff7fc29011be64031a24d03b7f37b617cfd2d"
+ },
+ {
+ "name": "bangbang",
+ "unicode": "203C",
+ "digest": "bdd350766ccd1c0138f6294f7ebfa3e9867b02bda40a743f7062e52c68358765"
+ },
+ {
+ "name": "bank",
+ "unicode": "1F3E6",
+ "digest": "c9648c93049cf8e7884242e58ae3145383d2e5034c9090e0d34c53f5bbce397f"
+ },
+ {
+ "name": "bar_chart",
+ "unicode": "1F4CA",
+ "digest": "942277f72a5b754b13454dab62c85b1ff3447544f38ec76a285f3be32f6f5d12"
+ },
+ {
+ "name": "barber",
+ "unicode": "1F488",
+ "digest": "e1526eea685aafc56fb83d07f8ff63c9967600e447b0e5f831a17d6153f2062d"
+ },
+ {
+ "name": "baseball",
+ "unicode": "26BE",
+ "digest": "3d028b16a898f3a15874bc9d3891f9fbf59ea1c226c5c774eddb58a712c489ae"
+ },
+ {
+ "name": "basketball",
+ "unicode": "1F3C0",
+ "digest": "b2f5a3904d505db066337a24fc840ef75b49ef4c5f152227d8e632ff82285b12"
+ },
+ {
+ "name": "basketball_player",
+ "unicode": "26F9",
+ "digest": "e94beb69f631667479a80095bf313ceb3aa109d6ebb80f182722360a6d2a214e"
+ },
+ {
+ "name": "basketball_player_tone1",
+ "unicode": "26F9-1F3FB",
+ "digest": "6fc77cf2f26ee18e9a3faea500d4277839f77633f31ee618a68c301f1ad32d90"
+ },
+ {
+ "name": "basketball_player_tone2",
+ "unicode": "26F9-1F3FC",
+ "digest": "6ee9060c24d92708e12a854fb0bdf5c717c90b8c0350d8aa40c278b41bfa12fc"
+ },
+ {
+ "name": "basketball_player_tone3",
+ "unicode": "26F9-1F3FD",
+ "digest": "752e90dbfa7c7a9ae3f37de924e22f3c3d5a7e54dd41c8e8eb99cabb0dad73cf"
+ },
+ {
+ "name": "basketball_player_tone4",
+ "unicode": "26F9-1F3FE",
+ "digest": "38bedc3074e6243454d568d9b665f5764f1a3d983875651ce7a1cdb53da9f6c8"
+ },
+ {
+ "name": "basketball_player_tone5",
+ "unicode": "26F9-1F3FF",
+ "digest": "25ee1e84670d3db96d3ad098c859abd6b3448f55f668ce0c195ee2337a215de7"
+ },
+ {
+ "name": "bath",
+ "unicode": "1F6C0",
+ "digest": "ae6301a6354630cd9dc06a5137f23f826d019c8298b2b012b6ff31b773a910b6"
+ },
+ {
+ "name": "bath_tone1",
+ "unicode": "1F6C0-1F3FB",
+ "digest": "fce7ae2e7ef3f7f44f36c2ad49348b4cf7fce0b0c17e1a90a1e85734cee95b2a"
+ },
+ {
+ "name": "bath_tone2",
+ "unicode": "1F6C0-1F3FC",
+ "digest": "4d1c9444f16467488fe939fdad279d6855d28be564e5dcc1990451c4b9ae8c95"
+ },
+ {
+ "name": "bath_tone3",
+ "unicode": "1F6C0-1F3FD",
+ "digest": "9a59a4360effb48af4cbb1a953655ef61e69375407038b4d0bd8068fbaf3cc16"
+ },
+ {
+ "name": "bath_tone4",
+ "unicode": "1F6C0-1F3FE",
+ "digest": "01aafa8a53a08018b9fbf28ec6b3b918d6bd0dee7a891196f32f81f60d114f0e"
+ },
+ {
+ "name": "bath_tone5",
+ "unicode": "1F6C0-1F3FF",
+ "digest": "2733e81ccaee21231c2e47e3310b431e9bd784bf34f0db609f8eadcee359500d"
+ },
+ {
+ "name": "bathtub",
+ "unicode": "1F6C1",
+ "digest": "9515e3bb9ab41350305e64fc6877aae82d51e1ba8ce8b2b4b8ffaeda960820cd"
+ },
+ {
+ "name": "battery",
+ "unicode": "1F50B",
+ "digest": "7d4d475c1d5b1be55c319953e3363ff864fe4fcd921a8aa649b9a547c0894deb"
+ },
+ {
+ "name": "beach",
+ "unicode": "1F3D6",
+ "digest": "52855d75cfa4476ccc23c58b4afcb76ee48abb22a9a6081210c8accefdf33099"
+ },
+ {
+ "name": "beach_umbrella",
+ "unicode": "26F1",
+ "digest": "cefe8e195d21d3e0769d3bfe15170db9e57c86db9d31cacb19fcdc8d2191b661"
+ },
+ {
+ "name": "bear",
+ "unicode": "1F43B",
+ "digest": "b5ac126875c20c82b9e3140b143233944a2e4132d781d0b575e83673988523cb"
+ },
+ {
+ "name": "bed",
+ "unicode": "1F6CF",
+ "digest": "1919245d7a76799aad0533eb72db2cbaa1f32ee8231a0c1989d3f233f2d42370"
+ },
+ {
+ "name": "bee",
+ "unicode": "1F41D",
+ "digest": "69ada63403c8dabae39c63ba143143aeb59b66faae6aa82d8342337925a9e6b5"
+ },
+ {
+ "name": "beer",
+ "unicode": "1F37A",
+ "digest": "b71dd6efdb4ce7d9d71fdbf82a2ccf83841fb0cceb119ee7da1e575d3bfa853c"
+ },
+ {
+ "name": "beers",
+ "unicode": "1F37B",
+ "digest": "994108cebfe0c614c05967af4e3864d8adbbfcf7cccef1cbd42a47b7dfabf80c"
+ },
+ {
+ "name": "beetle",
+ "unicode": "1F41E",
+ "digest": "ec351ce238a81711eef00e5be1de2e198423cf524b60e531d435902b44420edc"
+ },
+ {
+ "name": "beginner",
+ "unicode": "1F530",
+ "digest": "13288d9fc221dc02f4181b998104e13c3c5c98d3c4e650186bef59a46d39f6f0"
+ },
+ {
+ "name": "bell",
+ "unicode": "1F514",
+ "digest": "784b9a82814ce14a264e54b3a8f8e706f3c7b763646d9f8174c4aa84ad41ef09"
+ },
+ {
+ "name": "bellhop",
+ "unicode": "1F6CE",
+ "digest": "c15455f1b52ac26404b5c13a0e1070212ed1830026422873f4f6335e26e31259"
+ },
+ {
+ "name": "bento",
+ "unicode": "1F371",
+ "digest": "d59314b17a8646d4a78fefb7b79f289f33d4aaea893fed4cad0b890df63395e7"
+ },
+ {
+ "name": "bicyclist",
+ "unicode": "1F6B4",
+ "digest": "e7359d615d40325bb08a145cfebde2ecef448deeb21695a34b55d3ccb971447f"
+ },
+ {
+ "name": "bicyclist_tone1",
+ "unicode": "1F6B4-1F3FB",
+ "digest": "e45808faa32f4ffb881d3569c0b8e2c69d4a64665f4d1fae24d7a1e5f1d3ea4b"
+ },
+ {
+ "name": "bicyclist_tone2",
+ "unicode": "1F6B4-1F3FC",
+ "digest": "92a3494270d1da6a117e92402c7898d4a7fffbe3d6143fb9ae445c4827c0c8a4"
+ },
+ {
+ "name": "bicyclist_tone3",
+ "unicode": "1F6B4-1F3FD",
+ "digest": "6fdf1db2bbd08d06b643b08f0f29daeaa20e0b8c8abec21132191f435cc05e42"
+ },
+ {
+ "name": "bicyclist_tone4",
+ "unicode": "1F6B4-1F3FE",
+ "digest": "d9c27848e1bcc8197c858e1ef12a537f4ed6c77fb211b6731388dc88c2bb7a61"
+ },
+ {
+ "name": "bicyclist_tone5",
+ "unicode": "1F6B4-1F3FF",
+ "digest": "4892af1a8a0229a813d7b8e3d88481c2365e3e1a5ce2e0e27ce432c5336da810"
+ },
+ {
+ "name": "bike",
+ "unicode": "1F6B2",
+ "digest": "e726f97b5432f46ed51328c0930d1d63b3a2d7b67c5c2303a5ca997083cfcac1"
+ },
+ {
+ "name": "bikini",
+ "unicode": "1F459",
+ "digest": "7612fcb72c005ae7172260825f588d6995f2bc919cb3d283dd4591f6872a1855"
+ },
+ {
+ "name": "biohazard",
+ "unicode": "2623",
+ "digest": "81f8309318051255ed4dc18855a3cd3f8657a6f3b2d368caa531a57ce0e34235"
+ },
+ {
+ "name": "bird",
+ "unicode": "1F426",
+ "digest": "3f219e5aa18e2f1febfd368ec133786cd2eab357db79984cb8ba07fed0eec7cd"
+ },
+ {
+ "name": "birthday",
+ "unicode": "1F382",
+ "digest": "9eb1adb0170ab851042cb3da8b64f02f4e4b63e7a07db405b55b50f5bbd3cacf"
+ },
+ {
+ "name": "black_circle",
+ "unicode": "26AB",
+ "digest": "c2ba672994ad0f99d7fdc449f3fee45a2dca68a58f9fe95825b38465a30ef44e"
+ },
+ {
+ "name": "black_joker",
+ "unicode": "1F0CF",
+ "digest": "1eb85b8e2b93dec221a97a1c309dee3683408f6166e1a1a1bd83cf2f64f007dd"
+ },
+ {
+ "name": "black_large_square",
+ "unicode": "2B1B",
+ "digest": "0ff2112227c38ed8c30b0bddf2300e87d2a244cd7fe81886a1cb1a287a7e8bb6"
+ },
+ {
+ "name": "black_medium_small_square",
+ "unicode": "25FE",
+ "digest": "f1010aa694084ad4655a9d4ce5a1711eaab21029e31bf8798253f0ad644e8abb"
+ },
+ {
+ "name": "black_medium_square",
+ "unicode": "25FC",
+ "digest": "06bf48ffbc84e71bbb90aa0f6c3f9f53533c6fd063ff168cefdb0a050dcf8302"
+ },
+ {
+ "name": "black_nib",
+ "unicode": "2712",
+ "digest": "c1361df4a5ae9f2ed121d26928021e96c6865331861e1960700d39cb1bd49355"
+ },
+ {
+ "name": "black_small_square",
+ "unicode": "25AA",
+ "digest": "d430ec419869fa1b5ba980ddeecb4c5ad5050a2b3421e45048cc184a6fc46899"
+ },
+ {
+ "name": "black_square_button",
+ "unicode": "1F532",
+ "digest": "85b6587b6b2c3544ddb7bc07207b0740e437744ba134835836153899ae396135"
+ },
+ {
+ "name": "blossom",
+ "unicode": "1F33C",
+ "digest": "029bbe385e07e2017dd918d685e107678c9c0e919a3bd1521b7a0d7c9172da05"
+ },
+ {
+ "name": "blowfish",
+ "unicode": "1F421",
+ "digest": "b5ee9f6ffabb74e3024067f016d17a631ee98536cb9c7269d55fa867f95a54fb"
+ },
+ {
+ "name": "blue_book",
+ "unicode": "1F4D8",
+ "digest": "6fbf227fb9facc1957bb9dfb31749cbfe66c3afe8081347f2471fd64ef2e6b3a"
+ },
+ {
+ "name": "blue_car",
+ "unicode": "1F699",
+ "digest": "e61ef2299d11fc01e9d6c496d188a7211633946706f6e771c412368346ca16f4"
+ },
+ {
+ "name": "blue_heart",
+ "unicode": "1F499",
+ "digest": "1af8d04173e0a984360786f6031220000dd548b8c912a68fd51f2ba490a9e16a"
+ },
+ {
+ "name": "blush",
+ "unicode": "1F60A",
+ "digest": "d615cda0f7c185ed8a92008204043ef769f3b7fb5424d595aeaaf3827bcdbd73"
+ },
+ {
+ "name": "boar",
+ "unicode": "1F417",
+ "digest": "c23a06db0337597e361ae581eacd4faf9926c6b7db0510d3599eb2e2a73315cb"
+ },
+ {
+ "name": "bomb",
+ "unicode": "1F4A3",
+ "digest": "0099e7435eba35f4f3ad273993293693a8b5cd110567c95ed83e5b4e2d0978ff"
+ },
+ {
+ "name": "book",
+ "unicode": "1F4D6",
+ "digest": "152408f2ff9949b7cbe57f623e4f875aa8dd0b02317e03cc914e1ea3712b3fc7"
+ },
+ {
+ "name": "book2",
+ "unicode": "1F56E",
+ "digest": "26d6b66a1957e7750b3e22eb2e46d0cc85932977bbb81d3d8482ec1ec58ee12b"
+ },
+ {
+ "name": "bookmark",
+ "unicode": "1F516",
+ "digest": "a2e0c6f5466c1b2fc148b20f6afcf4a878f4df55b0181f61fffa3ff727dcb251"
+ },
+ {
+ "name": "bookmark_tabs",
+ "unicode": "1F4D1",
+ "digest": "16135d62ff440722bd1ce8f84219be6a5eb3120a1597bfda4aeed4a2d9e7d7b2"
+ },
+ {
+ "name": "books",
+ "unicode": "1F4DA",
+ "digest": "ba019e4174639440caec424b30dfa016fe71a6f7436fe63025a2e3609ebfc012"
+ },
+ {
+ "name": "boom",
+ "unicode": "1F4A5",
+ "digest": "ec26246935c99749950612d69c06435ccdc126f14426a48a7599c5b6b91d9d58"
+ },
+ {
+ "name": "boot",
+ "unicode": "1F462",
+ "digest": "7ed639d52e285b0f46064dd4e1f4a8fb5814e1b2dc47c6f93cb349a6ac7ea97a"
+ },
+ {
+ "name": "bouquet",
+ "unicode": "1F490",
+ "digest": "b699f13af218560344f3571436f87b6f8c5c9f0fa0308836937667241b3fc7aa"
+ },
+ {
+ "name": "bouquet2",
+ "unicode": "1F395",
+ "digest": "1643ec51ff26fc1ac0c67859e202386398650bf2a996c82b68e1b73fa52abf7d"
+ },
+ {
+ "name": "bow",
+ "unicode": "1F647",
+ "digest": "5e260c38cfc80cd2f20ef78d982126dbf90934f7afa12c96d0b7b413beb6d4e0"
+ },
+ {
+ "name": "bow_and_arrow",
+ "unicode": "1F3F9",
+ "digest": "1c23469256331ea4ff03c036f89f0e63ad3228c51faecba50129da99b7eaddf3"
+ },
+ {
+ "name": "bow_tone1",
+ "unicode": "1F647-1F3FB",
+ "digest": "d3ec7ef70b355ba310d6fae7130a4e4cd11526b6e219474b5678a2b3ba1077f0"
+ },
+ {
+ "name": "bow_tone2",
+ "unicode": "1F647-1F3FC",
+ "digest": "c2905c0feba15fbc533cc6b36038eeda30f729182aa544f1d9164f5ccfed64d5"
+ },
+ {
+ "name": "bow_tone3",
+ "unicode": "1F647-1F3FD",
+ "digest": "298fc646d96c307eaa137c80b403d8355539ed8af13d3954a4ccacef67d341fa"
+ },
+ {
+ "name": "bow_tone4",
+ "unicode": "1F647-1F3FE",
+ "digest": "27db8401aa62a2544b24ff839b332958b5e8c3ab3fd7a289d3c62c654705da60"
+ },
+ {
+ "name": "bow_tone5",
+ "unicode": "1F647-1F3FF",
+ "digest": "168cdf834edb54723cf1c32311d4117c288132c5f76d6c415726c7484158c52a"
+ },
+ {
+ "name": "bowling",
+ "unicode": "1F3B3",
+ "digest": "0e888bcd1a5cc1ea7b07cea255ccb04dcdc87b0337b74cdc96a708aad7975768"
+ },
+ {
+ "name": "boy",
+ "unicode": "1F466",
+ "digest": "f349ab3e1015b4ccda5faab6a355f9c38e36e7c1cd667084563a14a2b11036ea"
+ },
+ {
+ "name": "boy_tone1",
+ "unicode": "1F466-1F3FB",
+ "digest": "4d04a5e45c9f9749de580321a212e14304b4ffcd229fa971fb59d97e6124262f"
+ },
+ {
+ "name": "boy_tone2",
+ "unicode": "1F466-1F3FC",
+ "digest": "0c9d6b6b1b3da68b9ef1f0f01efa4d170a48cfc66de4f577f8669c160b81cc97"
+ },
+ {
+ "name": "boy_tone3",
+ "unicode": "1F466-1F3FD",
+ "digest": "7dbecace78edb2aceffce6cb4d49ca132b93d80c26a8f1526a18832a2f23454a"
+ },
+ {
+ "name": "boy_tone4",
+ "unicode": "1F466-1F3FE",
+ "digest": "49f9c633afa8ff81068c78717e0012f8936fb3dcdb8b57342410f57f0635ae7c"
+ },
+ {
+ "name": "boy_tone5",
+ "unicode": "1F466-1F3FF",
+ "digest": "17e2ec379c7b542e6c2c5deef992af5f1fbaa3e288d1f71c8c984fb91a698cd4"
+ },
+ {
+ "name": "boys_symbol",
+ "unicode": "1F6C9",
+ "digest": "47fadbcb876ca436264ce2f3ebd1472bd68f55cc2b4833bf054335be9dc7a0f2"
+ },
+ {
+ "name": "bread",
+ "unicode": "1F35E",
+ "digest": "43697495538bfed11ed75213af8b1bdc14ef359d9b472cd7f9130fcb0a198680"
+ },
+ {
+ "name": "bride_with_veil",
+ "unicode": "1F470",
+ "digest": "37e75fbb2b0d06c900d51269b99107c60b61453dbf218b54df3011a455cd6dc3"
+ },
+ {
+ "name": "bride_with_veil_tone1",
+ "unicode": "1F470-1F3FB",
+ "digest": "44072e54e0618d2675a5bfd6572108590e51e8e733381e091e8754ee96c2cf20"
+ },
+ {
+ "name": "bride_with_veil_tone2",
+ "unicode": "1F470-1F3FC",
+ "digest": "f0acd961e108db9d9dd5d1b06e708b2eb6a7ef7235d6c8678b9319077faf4fa8"
+ },
+ {
+ "name": "bride_with_veil_tone3",
+ "unicode": "1F470-1F3FD",
+ "digest": "3f7adddb41ead3cd07098799ab2a5b8e8842344307d9045264403fb685f20555"
+ },
+ {
+ "name": "bride_with_veil_tone4",
+ "unicode": "1F470-1F3FE",
+ "digest": "5f7199fd99319651f3a7b3553cc5387c59b65cac1eb020441e19b5c12c807dc7"
+ },
+ {
+ "name": "bride_with_veil_tone5",
+ "unicode": "1F470-1F3FF",
+ "digest": "4b1f6c33dd72a3a11c764bb00e7be7441b39c7af78aae52141276a279d63ab78"
+ },
+ {
+ "name": "bridge_at_night",
+ "unicode": "1F309",
+ "digest": "f81cc36de8edbdf3fe4d55932d5c6c8ad429487ec1f7af044611b6dc950ee09c"
+ },
+ {
+ "name": "briefcase",
+ "unicode": "1F4BC",
+ "digest": "a3c3e802191f3e131683dac1fcd81e294dea72af8e65c94972990924c79c5619"
+ },
+ {
+ "name": "broken_heart",
+ "unicode": "1F494",
+ "digest": "4dee349274c2ea44d1c0395cbd39356b88897b0c45040aa40d8cb2607ee67420"
+ },
+ {
+ "name": "bug",
+ "unicode": "1F41B",
+ "digest": "bac4660ee8dcbef0023691804ee3fad3ea3d4bac20d847a5913cee6e7dca826c"
+ },
+ {
+ "name": "bulb",
+ "unicode": "1F4A1",
+ "digest": "af5394230f95781c7eb8054b1a13732a6e6170318599c79e9ca2a816a5b821a2"
+ },
+ {
+ "name": "bullettrain_front",
+ "unicode": "1F685",
+ "digest": "59afcd289500bd4148b1b91f560a5ce8ac9e1b52eddb8fec857ff5d171f017fb"
+ },
+ {
+ "name": "bullettrain_side",
+ "unicode": "1F684",
+ "digest": "79ff8f579081a2f1c3b05311a18ca432adb026a7860875cea4a5460e49b2a474"
+ },
+ {
+ "name": "bullhorn",
+ "unicode": "1F56B",
+ "digest": "a4ca5cbfe299e8ccd148d17055d2d395cf8515e416bf771044c9a670509a8254"
+ },
+ {
+ "name": "bullhorn_waves",
+ "unicode": "1F56C",
+ "digest": "92493636cf086205d1e12cc19e613b84152ef10b8cd0215619a0fc813bfc9a7c"
+ },
+ {
+ "name": "burrito",
+ "unicode": "1F32F",
+ "digest": "4babb1af1136ab2334d26495b0be779d0bcc9516fd956fc07ffde427d11122f0"
+ },
+ {
+ "name": "bus",
+ "unicode": "1F68C",
+ "digest": "476e7a5e92f64038e5012205395efead51f1c10b3edb25380f38da97e2412edd"
+ },
+ {
+ "name": "busstop",
+ "unicode": "1F68F",
+ "digest": "3bcf82872ab6abb0278238c71bd004a40c46696bdda05f54c153d45d6fe88f15"
+ },
+ {
+ "name": "bust_in_silhouette",
+ "unicode": "1F464",
+ "digest": "2230844993ab011fe2756a1aa3873ff7d5f7d888bddec408ba0b32e4f6003570"
+ },
+ {
+ "name": "busts_in_silhouette",
+ "unicode": "1F465",
+ "digest": "d1c3cb6d437616834425a53621c0bc0a6b368d745dd9da2300a3db4543d57660"
+ },
+ {
+ "name": "cactus",
+ "unicode": "1F335",
+ "digest": "e87588e6548d201db903dc0523b3ccc83c6b559981d743eae1504ce668cd8be4"
+ },
+ {
+ "name": "cake",
+ "unicode": "1F370",
+ "digest": "3947783d128018f5e396602d0492cb5c31e8e8df98af01eda7cade71aea8d989"
+ },
+ {
+ "name": "calculator",
+ "unicode": "1F5A9",
+ "digest": "01b47b5c69c12b65fa4f4c0d580f2a98280d6116f4ad2cf8be378759008bcc3c"
+ },
+ {
+ "name": "calendar",
+ "unicode": "1F4C6",
+ "digest": "00bb700dd88efbc43bc64263491cdf77965130b1dc23f31e682905c3dfe4040c"
+ },
+ {
+ "name": "calendar_spiral",
+ "unicode": "1F5D3",
+ "digest": "1dd5da98bb435c0c3f632bc0a5c9fdde694de7aee752bf4bb85def086e788a2a"
+ },
+ {
+ "name": "calling",
+ "unicode": "1F4F2",
+ "digest": "2375828085f2efd17b8a5ebb3cfec1e420190913328a7a0dd9ff0f67c7249ffb"
+ },
+ {
+ "name": "camel",
+ "unicode": "1F42B",
+ "digest": "9ff789ab50b51cd9e7fdc7fbe8d6f913fda95dfd425949f97974548652a53ce1"
+ },
+ {
+ "name": "camera",
+ "unicode": "1F4F7",
+ "digest": "d95192b9ba0f566d8874099125def031e15297d1306989ea9b6a49f7b9b56661"
+ },
+ {
+ "name": "camera_with_flash",
+ "unicode": "1F4F8",
+ "digest": "4db6fb3fdb9a004537dff97f4197c7ed87c9c978ba9ac562ed8bb7c1fa260d38"
+ },
+ {
+ "name": "camping",
+ "unicode": "1F3D5",
+ "digest": "f0855dc78bf6f3d06b3c2fc19180c8ff23d9e22871658fcc26a8fde08d328a0a"
+ },
+ {
+ "name": "cancellation_x",
+ "unicode": "1F5D9",
+ "digest": "cea2f7a48543207615ee06755ded62c2a95a7eaf7d7b68a3fc25e74d94e2c92c"
+ },
+ {
+ "name": "cancer",
+ "unicode": "264B",
+ "digest": "b990f85e9f62017d99526244eaef5c5e56f8808698011e85d44de1d2ed87f1a2"
+ },
+ {
+ "name": "candle",
+ "unicode": "1F56F",
+ "digest": "5eefd555951e65298583009a307acc6fb6d02c88325ef3adf231717e75e5a333"
+ },
+ {
+ "name": "candy",
+ "unicode": "1F36C",
+ "digest": "f14203c408173fbb94b4ee69d6de67226a17dc51b0cbd776f62623ee03fd2eb3"
+ },
+ {
+ "name": "capital_abcd",
+ "unicode": "1F520",
+ "digest": "2a7cc876218b8c244b9802448ee25ce5004671a4f00ea950a636d8c3b766dbef"
+ },
+ {
+ "name": "capricorn",
+ "unicode": "2651",
+ "digest": "03a5fd064c10f47c7fd0ae318c573bb559c269b1b2d61b45aa5b8ce9b5fbd9df"
+ },
+ {
+ "name": "card_box",
+ "unicode": "1F5C3",
+ "digest": "7d760ae1d44e6f4b2aac00895ca86b5743f8b5ca157ec2bd21ce2665e50ad23a"
+ },
+ {
+ "name": "card_index",
+ "unicode": "1F4C7",
+ "digest": "150950903eccb468981c58b87ed7c1ba44e17f52627d695f660ce96b3d9d6e8e"
+ },
+ {
+ "name": "carousel_horse",
+ "unicode": "1F3A0",
+ "digest": "d6862085550fa139a147dceb1b2b9f950a08dcd01cecd8b8697f9c7992ca054e"
+ },
+ {
+ "name": "cartridge",
+ "unicode": "1F5AD",
+ "digest": "0b1625eea118060b51a70905c1eb3313ed632e989f70943eca16aa29fe8a34f2"
+ },
+ {
+ "name": "cat",
+ "unicode": "1F431",
+ "digest": "002208c0c9165971853ee05cd05513175a913376a462a345a939d73401c6acb7"
+ },
+ {
+ "name": "cat2",
+ "unicode": "1F408",
+ "digest": "fbdb726cc035f83784dcfe2d9adb85f8aeec429064aed5c5ca0b8be406068aa5"
+ },
+ {
+ "name": "cd",
+ "unicode": "1F4BF",
+ "digest": "bd4d4eef2cc0b1e4ee1f5280f922743e76f27d35836987801b2b48969eac17d8"
+ },
+ {
+ "name": "celtic_cross",
+ "unicode": "1F548",
+ "digest": "187aac988d7e02085a15f31c4cc0ff25127be5b088e354e65c7b1152bffb40ff"
+ },
+ {
+ "name": "chains",
+ "unicode": "26D3",
+ "digest": "a6a915d9c361e1564e13cf2d33ad5df3d684aa349b8dc5909e6343d67401beb9"
+ },
+ {
+ "name": "champagne",
+ "unicode": "1F37E",
+ "digest": "77395d3afe5cc10bfdc381120bae2ae4aefdaa96c529536413873a696c5fa713"
+ },
+ {
+ "name": "chart",
+ "unicode": "1F4B9",
+ "digest": "9fd5f8cd99988bbe0fabc89a0b23e28d1468641d2f9468e82b7148a1948d8236"
+ },
+ {
+ "name": "chart_with_downwards_trend",
+ "unicode": "1F4C9",
+ "digest": "6fe456d76c0a996c12049057b5d60129098a9deddfa2d133cff5c4400e4595a0"
+ },
+ {
+ "name": "chart_with_upwards_trend",
+ "unicode": "1F4C8",
+ "digest": "e83cc4cf4228bd77e030a19755b11cf75cf671f40973c23e240afa54d9de478e"
+ },
+ {
+ "name": "checkered_flag",
+ "unicode": "1F3C1",
+ "digest": "77501c2c66af31f72f5c05f21e87598cd59740b5cfc02926c66dc755bab3c3cf"
+ },
+ {
+ "name": "cheese",
+ "unicode": "1F9C0",
+ "digest": "5897036ba97b557868bb314fcee83b9d8a609c8447b270a0b3d34a29ce7496d1"
+ },
+ {
+ "name": "cherries",
+ "unicode": "1F352",
+ "digest": "5a0ba73039e4b56e3d16a1c70ad992f41af7a16f6d5ba4b5337bdf338276f0ff"
+ },
+ {
+ "name": "cherry_blossom",
+ "unicode": "1F338",
+ "digest": "b40533225291f539ffe97e4ab1d70d07e179b2f9345b2814355164d0407cf3bf"
+ },
+ {
+ "name": "chestnut",
+ "unicode": "1F330",
+ "digest": "6a2a37899d28326daf36965b343b2646492c2c0cee8871321cc17315d6252a9a"
+ },
+ {
+ "name": "chicken",
+ "unicode": "1F414",
+ "digest": "13d770684a11ea10c0ae7570a98c5dfafd4bfb78ac3f72f46729aef9060b85c0"
+ },
+ {
+ "name": "children_crossing",
+ "unicode": "1F6B8",
+ "digest": "654d2502c1edc57c5ab4237df76db3121f6b8735eb13d30bffd305605a083445"
+ },
+ {
+ "name": "chipmunk",
+ "unicode": "1F43F",
+ "digest": "1ae3c838450afcbbe8a96992481dde252e343ab83546d0789ebed81a78ca9188"
+ },
+ {
+ "name": "chocolate_bar",
+ "unicode": "1F36B",
+ "digest": "2486b7265048eb2294d6be0a0a8a4d6067df95721ace9d131d8f715a27ba8cf0"
+ },
+ {
+ "name": "christmas_tree",
+ "unicode": "1F384",
+ "digest": "454c08870eaa84283c19731ed3b10c4868d2e2f0cc44f2feba0de9ba4cc9c4e1"
+ },
+ {
+ "name": "church",
+ "unicode": "26EA",
+ "digest": "b62e838ffb0dfefeced1707359437b6815e0721783b549212282e08617402f6f"
+ },
+ {
+ "name": "cinema",
+ "unicode": "1F3A6",
+ "digest": "6df56f6a0008d0352740d1e045ffdb702e80c2a6d88b6db1a8bcd27eb3c12dcc"
+ },
+ {
+ "name": "circus_tent",
+ "unicode": "1F3AA",
+ "digest": "f8b7a7f4cf4f9efd20423acc30abb3a28e2a5183b3e39f5cc88e7e0ed7757d64"
+ },
+ {
+ "name": "city_dusk",
+ "unicode": "1F306",
+ "digest": "8779066dc9386d05c951b1df1753983c2937a5f3b84d5fc09ed0b172d4ef914e"
+ },
+ {
+ "name": "city_sunset",
+ "unicode": "1F307",
+ "digest": "c2530d12204eb518c5a3c8d7deba11170b1412fdf406aea05a69d4c026210d1b"
+ },
+ {
+ "name": "cityscape",
+ "unicode": "1F3D9",
+ "digest": "15251a708d50fc721bd67d8abb2a517c0bade196df3b736e21d79191d749241f"
+ },
+ {
+ "name": "cl",
+ "unicode": "1F191",
+ "digest": "104591d8e7b980cf38dcf8326d36c845384b7a4e6d94c49f36e9946484712a95"
+ },
+ {
+ "name": "clap",
+ "unicode": "1F44F",
+ "digest": "ed6ef8bb78ca1fa295b87222c440c6d5ba4f154f2752bf0d428941260d66aaac"
+ },
+ {
+ "name": "clap_tone1",
+ "unicode": "1F44F-1F3FB",
+ "digest": "57a1fd1fa2578c30b8a47abb84e81af5f5bbc6c301a5daf0c53d4d07b017e777"
+ },
+ {
+ "name": "clap_tone2",
+ "unicode": "1F44F-1F3FC",
+ "digest": "2ad4dcd513e55486f21151bf3792e1febf116574d238545b07b4290901430fdd"
+ },
+ {
+ "name": "clap_tone3",
+ "unicode": "1F44F-1F3FD",
+ "digest": "2d8c705d4fcc162fb65cd51e2c6683f1129ebc72fba13343533f64ede1c62687"
+ },
+ {
+ "name": "clap_tone4",
+ "unicode": "1F44F-1F3FE",
+ "digest": "40ffd41b2b4f59d0040e9d20497e57c4e47f18aeae43fcae02be5c2f50069102"
+ },
+ {
+ "name": "clap_tone5",
+ "unicode": "1F44F-1F3FF",
+ "digest": "be55df1ac7600ba086c2ef6ea223ebc62271fa47876c53ade1a1c0151fdc994c"
+ },
+ {
+ "name": "clapper",
+ "unicode": "1F3AC",
+ "digest": "a8748398f56fd2c1e6e87fe0c77edec444df7c7dd462d43dbcea6d8de97c81c5"
+ },
+ {
+ "name": "classical_building",
+ "unicode": "1F3DB",
+ "digest": "6a607b0666141b51d6e944b04f3f6188a5c026396e6105f1d2a5e6b6350cd66b"
+ },
+ {
+ "name": "clipboard",
+ "unicode": "1F4CB",
+ "digest": "4ca1a0b864a962b111d6bdb65373b779f3fff571ffd32d029666f9b708e1ab73"
+ },
+ {
+ "name": "clock",
+ "unicode": "1F570",
+ "digest": "c48314ccde8bf01acc2b1bc9a6b5aa7d796fc0c8769f80398bc74545fcef31ed"
+ },
+ {
+ "name": "clock1",
+ "unicode": "1F550",
+ "digest": "c0550fa0c385920cbdb775bdaaa5e812097a484c4a32e35ebbafe3a364a4a438"
+ },
+ {
+ "name": "clock10",
+ "unicode": "1F559",
+ "digest": "25651ac5520505f326457364428de3679cc22ca57278d4c54cc4b60420fa7b74"
+ },
+ {
+ "name": "clock1030",
+ "unicode": "1F565",
+ "digest": "dbf682bac968fc5a3959af2b96eaaa5ee78306f6341c43c1345b94bc561a3d04"
+ },
+ {
+ "name": "clock11",
+ "unicode": "1F55A",
+ "digest": "333732dd6c3184f257964bcf5a20a6111f9adb04560b5d12dc613636e846df5b"
+ },
+ {
+ "name": "clock1130",
+ "unicode": "1F566",
+ "digest": "005999cb37998adea1645d7df63b2705a42db3b4f1a734891d79af3e833764ff"
+ },
+ {
+ "name": "clock12",
+ "unicode": "1F55B",
+ "digest": "6690e591bec1751e1c5472e0bf52f66779b2113e5b8c6c578e65dbb83d091b16"
+ },
+ {
+ "name": "clock1230",
+ "unicode": "1F567",
+ "digest": "549f3921bcff7f330c5a41e6756d8c15601f1f8278b35b369148771c60be2a6f"
+ },
+ {
+ "name": "clock130",
+ "unicode": "1F55C",
+ "digest": "9332ef07a9dde8ccaa1e58a3e97edee0601a1152fc6d351b782816c838d2a408"
+ },
+ {
+ "name": "clock2",
+ "unicode": "1F551",
+ "digest": "9d1ec8fbdae627880e1c067c10d6a40f1e4494a246c77224b3cd7b287554c4b4"
+ },
+ {
+ "name": "clock230",
+ "unicode": "1F55D",
+ "digest": "3578a39c28695d4e617a648a1eb44e0bb5a8a11dcbe04fa2eb2aea0a60589067"
+ },
+ {
+ "name": "clock3",
+ "unicode": "1F552",
+ "digest": "c2e2a27301b6ac27dc359be590448eb1e65fe87211f1af30a473d8bde4f3db47"
+ },
+ {
+ "name": "clock330",
+ "unicode": "1F55E",
+ "digest": "7a77cf8cf9a98f4767a2dca1d3795be45938eee185db81120d85cedebe128899"
+ },
+ {
+ "name": "clock4",
+ "unicode": "1F553",
+ "digest": "0945c4199400d546350cfff25bc9e9160789d1cf9890b3318bdc462ac6cc9782"
+ },
+ {
+ "name": "clock430",
+ "unicode": "1F55F",
+ "digest": "9fdb6f1fa076c4c6a395dbf6db27499ee447b3558f3aa64d913686c360e428a8"
+ },
+ {
+ "name": "clock5",
+ "unicode": "1F554",
+ "digest": "855b3500eb6d20bb6e51d3a6c9d1a5131c06404c6c149841c7cca52201036428"
+ },
+ {
+ "name": "clock530",
+ "unicode": "1F560",
+ "digest": "a6ebd9f884d45a1f43650351a1f1da9724bc044d7da2f6d99ffb3d1fa0c31c5d"
+ },
+ {
+ "name": "clock6",
+ "unicode": "1F555",
+ "digest": "e38f9fc4f87f12ee602dcf2285d59dbc343fc0fc37662992cfe9866c20f58e87"
+ },
+ {
+ "name": "clock630",
+ "unicode": "1F561",
+ "digest": "735954a650791fc38c845c43998023e652d36e55534850e43952878b8804b2f1"
+ },
+ {
+ "name": "clock7",
+ "unicode": "1F556",
+ "digest": "2c4244ec4019e9624e6ea5a751bb735ab87bead33b1ea160265c81bba3c2f736"
+ },
+ {
+ "name": "clock730",
+ "unicode": "1F562",
+ "digest": "0bcf20e30be1bb23394696770301867e307f8e5014e0ed7d75ed96efe34d625d"
+ },
+ {
+ "name": "clock8",
+ "unicode": "1F557",
+ "digest": "af454047a1765ef1c8355969302a826d4c47f5c61a6ec47fdec3510a8003b0d8"
+ },
+ {
+ "name": "clock830",
+ "unicode": "1F563",
+ "digest": "e48b81dac055dc6d5f7832cf34368329c573d03b35bfe076fed1c6e6d48a82e7"
+ },
+ {
+ "name": "clock9",
+ "unicode": "1F558",
+ "digest": "f2a3d1bc029dc0e6406cdaa96542e77503e4cfb79d99c69cb454b8cf635a73fc"
+ },
+ {
+ "name": "clock930",
+ "unicode": "1F564",
+ "digest": "bb1b2b83052e8e6fb97c48c13bce0d950907e044eb2dabf21d7fed321f75110b"
+ },
+ {
+ "name": "clockwise_arrows",
+ "unicode": "1F5D8",
+ "digest": "67027b7e1a4d800a3ce7d731c21c098d1109d217159a27665eebb7e080fc2622"
+ },
+ {
+ "name": "closed_book",
+ "unicode": "1F4D5",
+ "digest": "afd6dae5fa0f59330fc2adb922e92b3410a33a80a2667651718c7dac588010bc"
+ },
+ {
+ "name": "closed_lock_with_key",
+ "unicode": "1F510",
+ "digest": "d0ed5c00f939111ce86f9c741b733b22e04ebbd871aa33da3eb0f46a6f38b707"
+ },
+ {
+ "name": "closed_umbrella",
+ "unicode": "1F302",
+ "digest": "3ef08b299f9170007a5433fe82d0953bf0f75b6685d0ce58972f9af032dc471a"
+ },
+ {
+ "name": "cloud",
+ "unicode": "2601",
+ "digest": "d1e7932551e85c6e86bfb3b41f0c936a6d0953bf9f9119b8cca3eaed22ac0c01"
+ },
+ {
+ "name": "cloud_lightning",
+ "unicode": "1F329",
+ "digest": "fc9c85cc95f9c456635692c974f72b6d40e14943824b8129a21c47265c3416f4"
+ },
+ {
+ "name": "cloud_rain",
+ "unicode": "1F327",
+ "digest": "f4406e62ed98f6141ab70736f6d5c540023e805396db0346ee6b7082c3f5e8e2"
+ },
+ {
+ "name": "cloud_snow",
+ "unicode": "1F328",
+ "digest": "948990cd13dd927917208c026089519fcf8e258a8a284684ace67c9a2f9a8149"
+ },
+ {
+ "name": "cloud_tornado",
+ "unicode": "1F32A",
+ "digest": "44753516d0bd05d47cfa6eb922aba570ba6a87f805f325772b2cff071460ead1"
+ },
+ {
+ "name": "clubs",
+ "unicode": "2663",
+ "digest": "5fd19fadd3b0887a6a59819ffbbe33a061055c043200700c31be30e14a5d36d5"
+ },
+ {
+ "name": "cocktail",
+ "unicode": "1F378",
+ "digest": "cf096ebe15b4053702d490cd96f04d565b4993529bcd6d8d50cb821200d1cd92"
+ },
+ {
+ "name": "coffee",
+ "unicode": "2615",
+ "digest": "6ea6128e353d9f74aee99caaaaa30c53f996fb242bf3bffb0fa92e6b4d373e57"
+ },
+ {
+ "name": "coffin",
+ "unicode": "26B0",
+ "digest": "b59772d7aa262c4d7433f9cdf76d50011f4c63421b730c8ab4a08675f730c39f"
+ },
+ {
+ "name": "cold_sweat",
+ "unicode": "1F630",
+ "digest": "f0d0057bf01db8d930f6e4632c5bf8d0b1bc709bcfb6463a1f1973b5f1d70a83"
+ },
+ {
+ "name": "comet",
+ "unicode": "2604",
+ "digest": "00252ec55d1846d95c8d4c704b35251232d9810029fc215a7da08262dd1f3541"
+ },
+ {
+ "name": "compression",
+ "unicode": "1F5DC",
+ "digest": "432fbe66e5e3c38ebfeb4eb03465667a1e1be868b4afe510ec95eadda6481bde"
+ },
+ {
+ "name": "computer",
+ "unicode": "1F4BB",
+ "digest": "99777be010488867c7872b2e235be7c35b1a6f28d92baa921b61ced5491c0257"
+ },
+ {
+ "name": "computer_old",
+ "unicode": "1F5B3",
+ "digest": "b27c30d74f205a8a3bd00a55ca17da7cf6ae3b65ae33e949755a4c6bd69a9fd3"
+ },
+ {
+ "name": "confetti_ball",
+ "unicode": "1F38A",
+ "digest": "e77d0c0970d3d12e123e548639fc0fa3ce41668667e4be55baefc09dfaa22cb0"
+ },
+ {
+ "name": "confounded",
+ "unicode": "1F616",
+ "digest": "0f51db64149151d3d7ae5dce08c9af3d064123524fa36fe1f51a78cbd966b6ea"
+ },
+ {
+ "name": "confused",
+ "unicode": "1F615",
+ "digest": "ed23587432c1be98356156784ca4fe0b374b7b3b371660d45cfb0a1efd44e322"
+ },
+ {
+ "name": "congratulations",
+ "unicode": "3297",
+ "digest": "2a46d640bf24fd4dc7649baf4b28c4adb30eda8d24d70eda07036c85b48195e0"
+ },
+ {
+ "name": "construction",
+ "unicode": "1F6A7",
+ "digest": "73fac9fb5eb91954b0f998f9d05fb953241eed988c134fa42477393159fa34fa"
+ },
+ {
+ "name": "construction_site",
+ "unicode": "1F3D7",
+ "digest": "0ff52e6adf1927d356b27be5fef6bad2ad842be05e3a0bd16a17efe78e5676d9"
+ },
+ {
+ "name": "construction_worker",
+ "unicode": "1F477",
+ "digest": "2be436fa7ad0a31e328fc6f776044bd1eec35c99541ced891792e3bef738d0a0"
+ },
+ {
+ "name": "construction_worker_tone1",
+ "unicode": "1F477-1F3FB",
+ "digest": "172cebc84f91237a85292c5ab0a105cc3abbb96e7423c4ae81feffd00bdb3b26"
+ },
+ {
+ "name": "construction_worker_tone2",
+ "unicode": "1F477-1F3FC",
+ "digest": "3e9b96ddfd639eefda99ad3a0ad26a28a0f2c8be72988c2bdbd648e6104638b6"
+ },
+ {
+ "name": "construction_worker_tone3",
+ "unicode": "1F477-1F3FD",
+ "digest": "11f83c565168dce5ac2387b873769d85ec4087171d6e92fc766c209ea06cd4f3"
+ },
+ {
+ "name": "construction_worker_tone4",
+ "unicode": "1F477-1F3FE",
+ "digest": "09e320e78e3a2940f0c5a0ef9a235ab72c51e053fd8ff433843fdb62571c8e70"
+ },
+ {
+ "name": "construction_worker_tone5",
+ "unicode": "1F477-1F3FF",
+ "digest": "7ac2a1a0038e7aefea889380be604a98255823587e90799165f7db39dd03a0cc"
+ },
+ {
+ "name": "control_knobs",
+ "unicode": "1F39B",
+ "digest": "9f10e578b410ff6aa7cc7fe806a0f1181893765303c0ca3867b652f1392a8a22"
+ },
+ {
+ "name": "contruction_site",
+ "unicode": "1F3D7",
+ "digest": "0ff52e6adf1927d356b27be5fef6bad2ad842be05e3a0bd16a17efe78e5676d9"
+ },
+ {
+ "name": "convenience_store",
+ "unicode": "1F3EA",
+ "digest": "1ff4351e4a4503f58ed5d35074a2112c681337e35ffe55332187481685573606"
+ },
+ {
+ "name": "cookie",
+ "unicode": "1F36A",
+ "digest": "5c78ce2e721b0a3767d6ce0b59c1e88fdf94a7edc94e98c4d6b7aadb5b2aeea7"
+ },
+ {
+ "name": "cool",
+ "unicode": "1F192",
+ "digest": "54a96697a5070388ce8364a5ee2e0d78a53acc8b4f6755b1359fd67252cc41e8"
+ },
+ {
+ "name": "cop",
+ "unicode": "1F46E",
+ "digest": "16bee252c2a133bcf57f6d7b8372a61364744a2f662acb90e2005732555135fa"
+ },
+ {
+ "name": "cop_tone1",
+ "unicode": "1F46E-1F3FB",
+ "digest": "2fc52f3ed735e327d12dadb15f9feb7b7f720fc6857b551548a2a84809053817"
+ },
+ {
+ "name": "cop_tone2",
+ "unicode": "1F46E-1F3FC",
+ "digest": "6208f3174ced4f07ba3820ba838b247d7438d69d86eb04927333e7436e56af7e"
+ },
+ {
+ "name": "cop_tone3",
+ "unicode": "1F46E-1F3FD",
+ "digest": "2427d30bdfe127be4d8c3870472cae191eece142c784a5c2809df938f43e7c53"
+ },
+ {
+ "name": "cop_tone4",
+ "unicode": "1F46E-1F3FE",
+ "digest": "6e73f8abdf816f3cb2728b971a5a8d006a236c1d71b2ee1788ab60329f406323"
+ },
+ {
+ "name": "cop_tone5",
+ "unicode": "1F46E-1F3FF",
+ "digest": "4b146465cc95ade7e9ca722e31a1b06311214dae8f7f4d95c6329d56c45b451f"
+ },
+ {
+ "name": "copyright",
+ "unicode": "00A9",
+ "digest": "8143583821085dfc8ac21079fe220288ba3a3b6ca3014dc5dc98b18da77589c1"
+ },
+ {
+ "name": "corn",
+ "unicode": "1F33D",
+ "digest": "0160502226b5f9af81763545f288dbbb20632039d7509f347c751cfdb49dc5b5"
+ },
+ {
+ "name": "couch",
+ "unicode": "1F6CB",
+ "digest": "a93fffed194b404200495abda8772bb35539cfc8499eb0a9bf09c508afad6676"
+ },
+ {
+ "name": "couple",
+ "unicode": "1F46B",
+ "digest": "97fe611a613216a1788f9bd88a9deb4714ee123a66b5fd3d0ac916fbb4da7304"
+ },
+ {
+ "name": "couple_mm",
+ "unicode": "1F468-2764-1F468",
+ "digest": "3ae6fbf3ba168256ea85c756ac1e7b83fdb8b780d33f06128ed80706ff627eea"
+ },
+ {
+ "name": "couple_with_heart",
+ "unicode": "1F491",
+ "digest": "d9701173a5e8dff052ab6a15a42494dbb61dc7146d3734c82916abc9c05f76db"
+ },
+ {
+ "name": "couple_ww",
+ "unicode": "1F469-2764-1F469",
+ "digest": "d2a2ec29c1a1234ea0aa1d9fc6707cf8be8bb36ea8b92523ffa1c3071bcf0b06"
+ },
+ {
+ "name": "couplekiss",
+ "unicode": "1F48F",
+ "digest": "e722730de82397da7c8f88d79319b391e8f01fbe4a9133850cc92ad34e77bd82"
+ },
+ {
+ "name": "cow",
+ "unicode": "1F42E",
+ "digest": "dcc1efef2f02588806a156ed43da959c587d4c576ff6badec77f820ed3ba507f"
+ },
+ {
+ "name": "cow2",
+ "unicode": "1F404",
+ "digest": "dcf59f92fd0a37b2ca720bcda606defa4357b58d8f4ad15c1288ad8d814b2bc7"
+ },
+ {
+ "name": "crab",
+ "unicode": "1F980",
+ "digest": "59d34a4e92326ebeab188d9e33b25c20f4d54d187c274713fa3256b03b9e662a"
+ },
+ {
+ "name": "crayon",
+ "unicode": "1F58D",
+ "digest": "0f3351c2e68a8d47d27b45a9901be6160de0f9a291bd8680df84d0fc679bcb31"
+ },
+ {
+ "name": "credit_card",
+ "unicode": "1F4B3",
+ "digest": "708c0e7008e06e5d1b3b4e68a7e0ada9f4ae22ab6c28285d81a340f913fd9a84"
+ },
+ {
+ "name": "crescent_moon",
+ "unicode": "1F319",
+ "digest": "0959f838a410e8bfeebf00aa9658df56e515dbd2361142021071e17244662bfc"
+ },
+ {
+ "name": "cricket",
+ "unicode": "1F3CF",
+ "digest": "00eb11254e887c71db5e8945ad211e9e0280f1e02f4b77a4799b64bba2bbe9b3"
+ },
+ {
+ "name": "crocodile",
+ "unicode": "1F40A",
+ "digest": "99abcb42264d40d2450aaca8c3759a019bfd600a311cf3027243f1ca200d4639"
+ },
+ {
+ "name": "cross",
+ "unicode": "271D",
+ "digest": "a6e3c345cf6aa2ce690b66454066b53ef5b1dab2ed635e21f1586b1dffc5df42"
+ },
+ {
+ "name": "cross_heavy",
+ "unicode": "1F547",
+ "digest": "2e37c26b9bad0beb019c7f3e7a3892352d0ad9ca1b90c4333d42e8d56680be70"
+ },
+ {
+ "name": "cross_white",
+ "unicode": "1F546",
+ "digest": "3452e667010d7e49a51d7e1f4ba8ed4f303e33ed43255a051e9a18832a1efba6"
+ },
+ {
+ "name": "crossbones",
+ "unicode": "1F571",
+ "digest": "f5e7ce293c1a3282711073e68f033a3876e8428d1218cb2f8294630f9124e584"
+ },
+ {
+ "name": "crossed_flags",
+ "unicode": "1F38C",
+ "digest": "d4da057db289bec83f0106a94c89bd0cd9b52c7c7f8bc69bc8cbce480d53e12b"
+ },
+ {
+ "name": "crossed_swords",
+ "unicode": "2694",
+ "digest": "f159978583fa77c73ba6de85d35c4195cbd55963e537bd2bfd8f98ab8ff3559a"
+ },
+ {
+ "name": "crown",
+ "unicode": "1F451",
+ "digest": "e6fe2a28b7d80749ca121cabbe89321dcecdd760a122e73fb1562ea9bb40e90d"
+ },
+ {
+ "name": "cruise_ship",
+ "unicode": "1F6F3",
+ "digest": "90519c46ddfb63e71bc76661953da9041e5f0b97e9f8a7a8696518b4d529f3dd"
+ },
+ {
+ "name": "cry",
+ "unicode": "1F622",
+ "digest": "2d6a096796222c29b050f74db6b5aff9b9f61390c5eb56e45d1801918751002f"
+ },
+ {
+ "name": "crying_cat_face",
+ "unicode": "1F63F",
+ "digest": "df057d4e3e5c5c87caedf87ea3a6f936811b93f228f46bb7018d2bb5afaa6d35"
+ },
+ {
+ "name": "crystal_ball",
+ "unicode": "1F52E",
+ "digest": "7de438f88134c32c4db67d705e5fecf2a6187a87f56ebbb5bcc5ba09626e2935"
+ },
+ {
+ "name": "cupid",
+ "unicode": "1F498",
+ "digest": "7cb3f7d1ddf9678982197ef0e65735fb465ae8e3652d611f37d3bcccf4d7e2c1"
+ },
+ {
+ "name": "curly_loop",
+ "unicode": "27B0",
+ "digest": "881a43ae406cb74b2ef136bf970db9928bcdc3bbbb7393e90d2c597fe1dd9a96"
+ },
+ {
+ "name": "currency_exchange",
+ "unicode": "1F4B1",
+ "digest": "c4d76e9e61fac8d3c0cb9e07f1fbf1a7fcac6f4d4c78776ff7f04fc9391ce689"
+ },
+ {
+ "name": "curry",
+ "unicode": "1F35B",
+ "digest": "ebe41ee864c873e3a371888c0087b11dbcb124335812895002ed81fe2b6ba571"
+ },
+ {
+ "name": "custard",
+ "unicode": "1F36E",
+ "digest": "afc192f405c30e2d529ec0f4b31a7faf474bcd01fded5294dc38880b8bb22155"
+ },
+ {
+ "name": "customs",
+ "unicode": "1F6C3",
+ "digest": "5abb98151a79cebc1032c0ea149617093e42f41e50574a790a91074cabaa4c3a"
+ },
+ {
+ "name": "cyclone",
+ "unicode": "1F300",
+ "digest": "ae77e15bf2f312f03dbc5c7813d304005bbb549953482db9beb91810c585dc0e"
+ },
+ {
+ "name": "dagger",
+ "unicode": "1F5E1",
+ "digest": "377060a7ce930566a4732b361be98e8a193a546846dfbba2a00abeeef41d1976"
+ },
+ {
+ "name": "dancer",
+ "unicode": "1F483",
+ "digest": "e050db55afbb968e02219a58c7e82b824848d299a4df64f0d08d4e1872816203"
+ },
+ {
+ "name": "dancer_tone1",
+ "unicode": "1F483-1F3FB",
+ "digest": "350f6b2e4589fdd436173163035621b8da0bd49c7b9ec9f39593aae5e0ed0641"
+ },
+ {
+ "name": "dancer_tone2",
+ "unicode": "1F483-1F3FC",
+ "digest": "a9efc84ec80582f286147ca34162a27fd5989f4030084acdbc309d4368660f5b"
+ },
+ {
+ "name": "dancer_tone3",
+ "unicode": "1F483-1F3FD",
+ "digest": "ef187f44278fdb8605c80f5cf199e0b3de8a49085dada2e215bb91e1d7d3be5d"
+ },
+ {
+ "name": "dancer_tone4",
+ "unicode": "1F483-1F3FE",
+ "digest": "5195bc352dc9d24cc5505a167c756038e55c05048c61799ea1bfdf2debe44ac2"
+ },
+ {
+ "name": "dancer_tone5",
+ "unicode": "1F483-1F3FF",
+ "digest": "55cb7eee9fa11a16a3932800a19e334546f7396df6aadde22e58fe3185926b16"
+ },
+ {
+ "name": "dancers",
+ "unicode": "1F46F",
+ "digest": "39e7dfd9dafeee20f2968960b1179ee4bf3f2b63a3035fc1944024d0ae8b5de1"
+ },
+ {
+ "name": "dango",
+ "unicode": "1F361",
+ "digest": "2a1b50abe5dc72335344878d9b701028ccad651964d9e3affeedbf3c2bfd652a"
+ },
+ {
+ "name": "dark_sunglasses",
+ "unicode": "1F576",
+ "digest": "6bb1e911a93d5eb0581d3ce8f8929125d3d8fc04e086f3263cfd25af1348ce6c"
+ },
+ {
+ "name": "dart",
+ "unicode": "1F3AF",
+ "digest": "6f28741543a4c1eead21856128ffea1fcf772954fe6af40844dfde47f092ed32"
+ },
+ {
+ "name": "dash",
+ "unicode": "1F4A8",
+ "digest": "25aef37611f1c2f2e96518bf8aeba80580dca9634c8505d390c147388adf6746"
+ },
+ {
+ "name": "date",
+ "unicode": "1F4C5",
+ "digest": "de591b8fad608be761b839beefe9e4c2316320bcf0c44c543a1bc4b89923d938"
+ },
+ {
+ "name": "deciduous_tree",
+ "unicode": "1F333",
+ "digest": "ff31a52096ac1eae770f7f71b6d802198add2c8b4d9d7c9327071b6d6ab86c7b"
+ },
+ {
+ "name": "department_store",
+ "unicode": "1F3EC",
+ "digest": "c1e200d5fdd792121acabdb17bbcfe8e28a63757cfd895c72d4909f14de95ac2"
+ },
+ {
+ "name": "descending_notes",
+ "unicode": "1F39D",
+ "digest": "f09c6a2e094b13bf91cc07b7b776e43348ccef9f91247ca36cc02e7d91098af0"
+ },
+ {
+ "name": "desert",
+ "unicode": "1F3DC",
+ "digest": "e45815250bfc5411de516f87efa218874bcda4b0420b4c17182efc22ba0ce80d"
+ },
+ {
+ "name": "desktop",
+ "unicode": "1F5A5",
+ "digest": "ba46323e695918e7253f1013cb991efb09790581c74c07c38bc5e10a20b8e8de"
+ },
+ {
+ "name": "desktop_window",
+ "unicode": "1F5D4",
+ "digest": "d5b6c4a847e2a96f97f50fd353a22cb121915cb1d7bbc0f02df38769819b6b7e"
+ },
+ {
+ "name": "diamond_shape_with_a_dot_inside",
+ "unicode": "1F4A0",
+ "digest": "4e0e6364b8682dec9a9e20676161c9c9c0faf0a5fdd5402ca2668b18f2bb850a"
+ },
+ {
+ "name": "diamonds",
+ "unicode": "2666",
+ "digest": "42b13b2ed8e5fc63fbe81263c06cc203ba18a45ed5cc2a4fdbf617d219a0d3b4"
+ },
+ {
+ "name": "disappointed",
+ "unicode": "1F61E",
+ "digest": "7f1a619fef84960a9f312d17a58aa58105a4f20a4072efb10227892ab22475d8"
+ },
+ {
+ "name": "disappointed_relieved",
+ "unicode": "1F625",
+ "digest": "a389f5e0a4b619dbc406217967fb1f8f3d0e49b3f790e554ae0ececadbf98967"
+ },
+ {
+ "name": "dividers",
+ "unicode": "1F5C2",
+ "digest": "bf4c303452a4c0b4986925041dbec5b7e478060d560630b7c5bc2f997fcad668"
+ },
+ {
+ "name": "dizzy",
+ "unicode": "1F4AB",
+ "digest": "d6fba9b906f0eabd46686e416273a2ca6634249374385f2abf7ed284f0eef995"
+ },
+ {
+ "name": "dizzy_face",
+ "unicode": "1F635",
+ "digest": "b55e20c1551a2912bb5ec64a66c788c9d6f21594cc1da66032188f3814b03f40"
+ },
+ {
+ "name": "do_not_litter",
+ "unicode": "1F6AF",
+ "digest": "126f8c4085e0a8de8241f211f96c3f42c3e3400ea7d8fdf79a14443c3eceb972"
+ },
+ {
+ "name": "document",
+ "unicode": "1F5CE",
+ "digest": "2cbca96cc69306a10f1a9b6505723e027239439d899f6b395dc43f3c37d2d777"
+ },
+ {
+ "name": "document_text",
+ "unicode": "1F5B9",
+ "digest": "29407b12409c9673f3d89ef1f86ee50cbc7ed53b1870e33b4a29bbc609017f72"
+ },
+ {
+ "name": "dog",
+ "unicode": "1F436",
+ "digest": "c7b729de8a0967b1f38c3fa5ded94e77e329588caeaaf43abfd1090f420e62bf"
+ },
+ {
+ "name": "dog2",
+ "unicode": "1F415",
+ "digest": "e1897ca60bb3d2662cbe7933352e2b9c50739adf5901d3328797bf399575b97a"
+ },
+ {
+ "name": "dollar",
+ "unicode": "1F4B5",
+ "digest": "7db1e57f799439df1295d42b5249393f1e8cacc8df54caf30499c967a7282742"
+ },
+ {
+ "name": "dolls",
+ "unicode": "1F38E",
+ "digest": "398e7ff5780328700aadded7ce8c50757b1096af5cec66cc4d813a6714686b6d"
+ },
+ {
+ "name": "dolphin",
+ "unicode": "1F42C",
+ "digest": "27385af08848d93acdd13f72751074c2cbccb5ab3c6047e334598af74ed4862d"
+ },
+ {
+ "name": "door",
+ "unicode": "1F6AA",
+ "digest": "3365d7834086328ecbf1da0037f1cf1d0eb49534e173f7962a9e8f4b2ab87e26"
+ },
+ {
+ "name": "doughnut",
+ "unicode": "1F369",
+ "digest": "b4b99fdfe8d07b49cbdd78f8c57e4424819a4ffc8a3ba4867da44cbb3b3a5cca"
+ },
+ {
+ "name": "dove",
+ "unicode": "1F54A",
+ "digest": "4e2e9c47e5632efe6ccf945d61dbc2f1155a2e52905e17f307b502a2c951bdb8"
+ },
+ {
+ "name": "dragon",
+ "unicode": "1F409",
+ "digest": "d7d016568b54d67017681a075fb799d4a2a790ecfa2946d02dbcee629eb4975d"
+ },
+ {
+ "name": "dragon_face",
+ "unicode": "1F432",
+ "digest": "4d0025f1df63b62448477a8f08a50704e15caafb10fea476b529113f41797ab9"
+ },
+ {
+ "name": "dress",
+ "unicode": "1F457",
+ "digest": "02d56ed227280eaf5ad92830ee304afb81f74bb5a13c855397bcd04dd7fa51fb"
+ },
+ {
+ "name": "dromedary_camel",
+ "unicode": "1F42A",
+ "digest": "5afe8a0b73f9f4560264020b1e02a566149dbc38c15a00d2fb5cd90b32d09a75"
+ },
+ {
+ "name": "droplet",
+ "unicode": "1F4A7",
+ "digest": "a92c419792cbd3ba90ed21547362134cfac3e17a5304ee4e3872c9f7b561f834"
+ },
+ {
+ "name": "dvd",
+ "unicode": "1F4C0",
+ "digest": "1ba23e2f01ced5e192e4c1d2f766d9bce400470e81c81410139fd3c0739422df"
+ },
+ {
+ "name": "e-mail",
+ "unicode": "1F4E7",
+ "digest": "12135310cfedc091d120426f5b132df82b538c5fcad458bf6b21588f353c3adb"
+ },
+ {
+ "name": "ear",
+ "unicode": "1F442",
+ "digest": "70ba1103a34e68590d91a3b6f8acdbad3b1c65e46e31e26ee1cb855c1e21095e"
+ },
+ {
+ "name": "ear_of_rice",
+ "unicode": "1F33E",
+ "digest": "ddd5f3cc83dbdafd9115861eecd0128e52165bb1dd0049df06ffc564b650d384"
+ },
+ {
+ "name": "ear_tone1",
+ "unicode": "1F442-1F3FB",
+ "digest": "72977be94f5d287a09d175f98fba8b7955ae13aa12ce8e029c0ca875c02ee820"
+ },
+ {
+ "name": "ear_tone2",
+ "unicode": "1F442-1F3FC",
+ "digest": "5ff2e46cb3be7f13b8b94092246b58dab4c2a9ee2a5a46e0b84cf35a6928141f"
+ },
+ {
+ "name": "ear_tone3",
+ "unicode": "1F442-1F3FD",
+ "digest": "19b523f5ada2acaea94b922059c458a3303f4da1dd4c197cf25d31a0e6ecc4b2"
+ },
+ {
+ "name": "ear_tone4",
+ "unicode": "1F442-1F3FE",
+ "digest": "6a5cca9f49c539ef7d0883a2f39652f33ee2d3b25dca0234e4ba027ebbb2b466"
+ },
+ {
+ "name": "ear_tone5",
+ "unicode": "1F442-1F3FF",
+ "digest": "a0a56e8abd36e9be6e2448bcee6f56ecb8bf62d728b19ab6e8f9c6338e226b67"
+ },
+ {
+ "name": "earth_africa",
+ "unicode": "1F30D",
+ "digest": "d4921b543d7cf0c7344fa50c5e4d5a76c208d900be852adc1ee82ed4e8861a39"
+ },
+ {
+ "name": "earth_americas",
+ "unicode": "1F30E",
+ "digest": "61691e6aa9b8d90fc7f75fbc6cc7add5c36022d38f3e05c9d7c54dc44cf865bb"
+ },
+ {
+ "name": "earth_asia",
+ "unicode": "1F30F",
+ "digest": "262904cb552c7f5cf828a11071b3d430a74824b7464e8759ef93ee23b1705767"
+ },
+ {
+ "name": "egg",
+ "unicode": "1F373",
+ "digest": "a7dd617cad489c481ffd14937d9ed491cdd5756903e00473f42600c2fbefb600"
+ },
+ {
+ "name": "eggplant",
+ "unicode": "1F346",
+ "digest": "e5402e8ae5b7f9699ed86b97c242f7939d5731c5a364a2d5b9d04ea5d293cda1"
+ },
+ {
+ "name": "eight",
+ "unicode": "0038-20E3",
+ "digest": "34e293d3228e4643a0132d592f96db91b651fe6ced056ac3c8a3fd49c5ed3416"
+ },
+ {
+ "name": "eight_pointed_black_star",
+ "unicode": "2734",
+ "digest": "c3c2da75731a9a0f4f0a8d1f9cffef75c35e19b7f5d4081da33ac12b46be5fc2"
+ },
+ {
+ "name": "eight_spoked_asterisk",
+ "unicode": "2733",
+ "digest": "cc69618c1074d2b00e6f2c49df5e2c5ff6f4c0fae305505eb8c9daa65a0ea340"
+ },
+ {
+ "name": "electric_plug",
+ "unicode": "1F50C",
+ "digest": "732e1d1675233a0b4643cb73d0c352f8a5a56a11ee90d26627ad1e43c2e4a8e5"
+ },
+ {
+ "name": "elephant",
+ "unicode": "1F418",
+ "digest": "08df3910c4d5d8f49a72c47dd938195e495bde8fd8b3e7b17098a2c1afc41634"
+ },
+ {
+ "name": "end",
+ "unicode": "1F51A",
+ "digest": "05844ab9dcb43deff86f04617af6ea09215595de1415dcfaae018bced57938fe"
+ },
+ {
+ "name": "envelope",
+ "unicode": "2709",
+ "digest": "aad272511d0db910437ba25cf1fb9c806d47aad92a232edb87055916daf4676a"
+ },
+ {
+ "name": "envelope_back",
+ "unicode": "1F582",
+ "digest": "bc60b6d375feee00758a94a05b42eeb165f4084b20eb3e6012b72faa221f7e75"
+ },
+ {
+ "name": "envelope_flying",
+ "unicode": "1F585",
+ "digest": "9d6b6ca4c08006062a6f11948de3e15b13cf5c458967e39a9358665a8e13e214"
+ },
+ {
+ "name": "envelope_stamped",
+ "unicode": "1F583",
+ "digest": "f6102aea7283ddc136bfeb09589573420b9279105045fc6b965c1633c1297468"
+ },
+ {
+ "name": "envelope_stamped_pen",
+ "unicode": "1F586",
+ "digest": "80ea471318d1e04f8e525ff236b3cd4a4c864e66c6246b6aad77d92f56895f33"
+ },
+ {
+ "name": "envelope_with_arrow",
+ "unicode": "1F4E9",
+ "digest": "c1ba19b5e7cf64c547ac46eee139e6af70700d49ab511a96e6828c30feb116bc"
+ },
+ {
+ "name": "euro",
+ "unicode": "1F4B6",
+ "digest": "f571952583ffecfa5777065e4d1b680c423d25bc80e567a48fb5d7a1c1b5e735"
+ },
+ {
+ "name": "european_castle",
+ "unicode": "1F3F0",
+ "digest": "db82e383975d079a7bb006e7868035088d75c33bd4031cf8466b71089b65426f"
+ },
+ {
+ "name": "european_post_office",
+ "unicode": "1F3E4",
+ "digest": "d9b38e0f0ca3ad8895b40c767bdbb2b142ccaf03a86c2f275f57a31ed478801a"
+ },
+ {
+ "name": "evergreen_tree",
+ "unicode": "1F332",
+ "digest": "60d8b2d86b20255341f7ecad6d0f178ba9db5fa6b3de92f1b439cdb19f2fc0b1"
+ },
+ {
+ "name": "exclamation",
+ "unicode": "2757",
+ "digest": "cd900ecf82de2b26f0d7783dac4b3232ae94d2cddad5bfacea2eaf65b7ac0a09"
+ },
+ {
+ "name": "expressionless",
+ "unicode": "1F611",
+ "digest": "2ec9466b2d629907ce4c3e24e57f7ee556d2258ff011d972e14d0ae969a40c51"
+ },
+ {
+ "name": "eye",
+ "unicode": "1F441",
+ "digest": "790841e8fce647173eec3c5019440ad9c7e916c535f92acb3132bd92df148cad"
+ },
+ {
+ "name": "eye_in_speech_bubble",
+ "unicode": "1F441-1F5E8",
+ "digest": "bcde5a89a7653bff302685d9d632dd2723796a7ac73125fb7b9493d1ca848e0a"
+ },
+ {
+ "name": "eyeglasses",
+ "unicode": "1F453",
+ "digest": "fd140bef19c420bafe59368d35dd58a58a53e7145b104bae94be10f90679213b"
+ },
+ {
+ "name": "eyes",
+ "unicode": "1F440",
+ "digest": "57ed1f87ebe2485ea32ea69abdb8c5f7ccdcc149b33e74230d801f0883c68c5d"
+ },
+ {
+ "name": "factory",
+ "unicode": "1F3ED",
+ "digest": "6e6b35ae013e5dd26852c9a95d05c39e89c1c1950a33f47e7b951c34af18f37c"
+ },
+ {
+ "name": "fallen_leaf",
+ "unicode": "1F342",
+ "digest": "28ba8628065ffa973b525dd1455691c828d49c2b8c814af387880c13f6707f7e"
+ },
+ {
+ "name": "family",
+ "unicode": "1F46A",
+ "digest": "b5307f86e54cfea581e8406f4b95c801e250a893a9d208cc9a69a6d910b90932"
+ },
+ {
+ "name": "family_mmb",
+ "unicode": "1F468-1F468-1F466",
+ "digest": "49a753c3fcd4420800dd1cda585dae6bfa81615ad4862b477246456f86dc9e82"
+ },
+ {
+ "name": "family_mmbb",
+ "unicode": "1F468-1F468-1F466-1F466",
+ "digest": "882a3a0048efd666b0ab3a07b9f08041aa3a2acdab02664d0feff30bbfa70d68"
+ },
+ {
+ "name": "family_mmg",
+ "unicode": "1F468-1F468-1F467",
+ "digest": "45dd75c19d260a658c8ac93cf878976b96d2000f0efc9c59e72dacc80afb08fa"
+ },
+ {
+ "name": "family_mmgb",
+ "unicode": "1F468-1F468-1F467-1F466",
+ "digest": "910f44a348a951d36ee1f1484d237085bec5083c3875a4d908831dfc64530eaf"
+ },
+ {
+ "name": "family_mmgg",
+ "unicode": "1F468-1F468-1F467-1F467",
+ "digest": "012e75ad0d1b16c2ce63bf80a1ebfb1fc194229cfaf1241039599b82832f6aee"
+ },
+ {
+ "name": "family_mwbb",
+ "unicode": "1F468-1F469-1F466-1F466",
+ "digest": "049a32f61c54f093d2124e25f8b2ec7eac13161e2f2ebf6dc067797698cbe831"
+ },
+ {
+ "name": "family_mwg",
+ "unicode": "1F468-1F469-1F467",
+ "digest": "ba32c637caba634bda99ccba2a1a2a4b6f33aaaed933c30c7d5a51e8de1790d0"
+ },
+ {
+ "name": "family_mwgb",
+ "unicode": "1F468-1F469-1F467-1F466",
+ "digest": "198faba987f45429329b93bbce4f111329f284558bf0eecfa1424186b5f009fe"
+ },
+ {
+ "name": "family_mwgg",
+ "unicode": "1F468-1F469-1F467-1F467",
+ "digest": "3fa2e57cba314dcff04cf8186914823e1e081aabf34fa7437b05c58015df400c"
+ },
+ {
+ "name": "family_wwb",
+ "unicode": "1F469-1F469-1F466",
+ "digest": "b9592fc110a25a478569075deaa520308ef74579cd47aa44df9836599d68143f"
+ },
+ {
+ "name": "family_wwbb",
+ "unicode": "1F469-1F469-1F466-1F466",
+ "digest": "88f398997835fcf5153f17f6baf0deeb2a9c25ce2f8422192c18ac23e90b3193"
+ },
+ {
+ "name": "family_wwg",
+ "unicode": "1F469-1F469-1F467",
+ "digest": "c8d859d3c957fe0d535efccde295fe99bab76e3d28ab5a49c8e736608461cb2e"
+ },
+ {
+ "name": "family_wwgb",
+ "unicode": "1F469-1F469-1F467-1F466",
+ "digest": "006506e4a3d0c82642a0c8481ce95e5e3b969e20fe2def0a16dd686afddbc705"
+ },
+ {
+ "name": "family_wwgg",
+ "unicode": "1F469-1F469-1F467-1F467",
+ "digest": "2553f0deab133aad09b99411d9dd68b56fede30f55ee1f354358767765e36673"
+ },
+ {
+ "name": "fast_forward",
+ "unicode": "23E9",
+ "digest": "1baaed10969b60c083da754ee056bb71df36182cc65af40640acfb76f6b39200"
+ },
+ {
+ "name": "fax",
+ "unicode": "1F4E0",
+ "digest": "b0a392192d03bd5d1ad5ee8eea933cf64725b1776819537bbed27561d78192e7"
+ },
+ {
+ "name": "fearful",
+ "unicode": "1F628",
+ "digest": "7c4cc4de3357c2a6d6e779342b09dabb3ef832a32f2778a0ba074b446f588e8f"
+ },
+ {
+ "name": "feet",
+ "unicode": "1F43E",
+ "digest": "cae13fb54ec64dbcf86ea25bebe2b79877e2d4f5d810b867f095f1d3dfc7f144"
+ },
+ {
+ "name": "ferris_wheel",
+ "unicode": "1F3A1",
+ "digest": "a710a8a0fb039d953313b75330db37e3228d856593547b1f04dc83c00168b987"
+ },
+ {
+ "name": "ferry",
+ "unicode": "26F4",
+ "digest": "21ea239b5adb68dc1ce6c5a1993b0a0b835ef6cc7a0a27cb890838d8475504f6"
+ },
+ {
+ "name": "field_hockey",
+ "unicode": "1F3D1",
+ "digest": "1e46c7f0b5b79c90a5d211ea14cd7e358b1a26a3c8294439253f2b08d0e5c92e"
+ },
+ {
+ "name": "file_cabinet",
+ "unicode": "1F5C4",
+ "digest": "c0b7bdab6c98909eb0fbf1ac89da0008bb00ddb1cb57fe64b4a5ac993eeb18c9"
+ },
+ {
+ "name": "file_folder",
+ "unicode": "1F4C1",
+ "digest": "d98f93c6d7283df0c45f08d3d31ecf5b91b6db1b735959f19e42bfada500a0d1"
+ },
+ {
+ "name": "film_frames",
+ "unicode": "1F39E",
+ "digest": "754a0a60e978f8299a0c4f8959e1f9260f01683e15ae943db430036f01a79b18"
+ },
+ {
+ "name": "finger_pointing_down",
+ "unicode": "1F597",
+ "digest": "0c542ac3141e8f2e74767acd0eb399c2d68c779cb78bf16d437ad3b1f8134ad9"
+ },
+ {
+ "name": "finger_pointing_down2",
+ "unicode": "1F59F",
+ "digest": "c5b128a232cbf518544802a2ae1459368274297163721fa05d0103cf95b2b1ee"
+ },
+ {
+ "name": "finger_pointing_left",
+ "unicode": "1F598",
+ "digest": "d178ece691e2091be08db77fda9cf05462934628557358a8cb6222587b291f7e"
+ },
+ {
+ "name": "finger_pointing_right",
+ "unicode": "1F599",
+ "digest": "a412a47544d8f401f9181f8826c5fa3d6b42a1d76f6926963c2d9cd2a01be06d"
+ },
+ {
+ "name": "finger_pointing_up",
+ "unicode": "1F59E",
+ "digest": "32c2ccab52aa318a47c816d1bcf9c076e667c9ef3e64ce37d7ba7e827238690d"
+ },
+ {
+ "name": "fire",
+ "unicode": "1F525",
+ "digest": "b44311874681135acbb5e7226febe4365c732da3a9617f10d7074a3b1ade1641"
+ },
+ {
+ "name": "fire_engine",
+ "unicode": "1F692",
+ "digest": "3ae03fa34a7088ada95458eb4ee3e97691b3489149f6bbc168086f0483ed3bb2"
+ },
+ {
+ "name": "fire_engine_oncoming",
+ "unicode": "1F6F1",
+ "digest": "e2482c450136d373f74dfafddf502e0b675eb5d2e1e1c645f163db0e4d15fbb6"
+ },
+ {
+ "name": "fireworks",
+ "unicode": "1F386",
+ "digest": "3dee83a27c406960253ca1460eb88a599c7b81506051b69605a421b17fe8282c"
+ },
+ {
+ "name": "first_quarter_moon",
+ "unicode": "1F313",
+ "digest": "8fa066362d77bd889090bbe0904ca47f34704e29781c67133c6eaa521c3e1972"
+ },
+ {
+ "name": "first_quarter_moon_with_face",
+ "unicode": "1F31B",
+ "digest": "8877edb366f8eaa00fd83200acf5a17c3b84d246a250519d565dda3aea866ec3"
+ },
+ {
+ "name": "fish",
+ "unicode": "1F41F",
+ "digest": "9ce742108794cc15e59f7719623ae938efbd8155c93ad72585a32f4e32ea9414"
+ },
+ {
+ "name": "fish_cake",
+ "unicode": "1F365",
+ "digest": "1b5b14509287e30da9b8d7abcec376b247f9095aea4bf3fc320349f061a4c321"
+ },
+ {
+ "name": "fishing_pole_and_fish",
+ "unicode": "1F3A3",
+ "digest": "35db56776db1fcec7c8479922d57d54da2577cfe44a894bfd78c51c950c450fb"
+ },
+ {
+ "name": "fist",
+ "unicode": "270A",
+ "digest": "6b80ac2e4d8b830ae06f7c1626d456460094e4ba20c20fb82dabb6b3d2ce7605"
+ },
+ {
+ "name": "fist_tone1",
+ "unicode": "270A-1F3FB",
+ "digest": "d7c79f4f988dd68f064baa5a3a568ab299f8d409db45c8463f39b80e5dd6081f"
+ },
+ {
+ "name": "fist_tone2",
+ "unicode": "270A-1F3FC",
+ "digest": "d1108194e2d962f9ccd00131876d769a8e003117a460d18b2ccbf93e0a0ea346"
+ },
+ {
+ "name": "fist_tone3",
+ "unicode": "270A-1F3FD",
+ "digest": "12f5644b632c95a5c2e41cc9af299e286e266db8b3860091ef5be5f0c4ccc026"
+ },
+ {
+ "name": "fist_tone4",
+ "unicode": "270A-1F3FE",
+ "digest": "521a3ac573381f3bc37a08ddd2d122767aaa0b6b7a38050d3671a12343351816"
+ },
+ {
+ "name": "fist_tone5",
+ "unicode": "270A-1F3FF",
+ "digest": "604e5a234da1b9160e506b3c9026faf9e04268fced7b44baa1ef5e3d4efa83a4"
+ },
+ {
+ "name": "five",
+ "unicode": "0035-20E3",
+ "digest": "0cbd6cd11eb6c2d67749112750d125f4f0a07b53bb7bfb1de0986d943ea9d632"
+ },
+ {
+ "name": "flag_ac",
+ "unicode": "1F1E6-1F1E8",
+ "digest": "d9db1edeb709824a1083c2bba79ca5f683ed0edded35918bb167d1ee7396c8da"
+ },
+ {
+ "name": "flag_ad",
+ "unicode": "1F1E6-1F1E9",
+ "digest": "04a8c1745d9b8b20e903302379f2557e8082f72e33878db4cb2cd6b33eb97952"
+ },
+ {
+ "name": "flag_ae",
+ "unicode": "1F1E6-1F1EA",
+ "digest": "868324ac2e7bea1547f5de95f39633b77b8d62f3b3433b3d1a4ee96d169a09cd"
+ },
+ {
+ "name": "flag_af",
+ "unicode": "1F1E6-1F1EB",
+ "digest": "9a94458519e9db5d6cf1557e54fdf62d7e48aaf7de25744a093ec8f284656226"
+ },
+ {
+ "name": "flag_ag",
+ "unicode": "1F1E6-1F1EC",
+ "digest": "ea59fabc2bd9024df06a59a34412f52bebfeb03eb6abd73d8fe153e3a68e28f4"
+ },
+ {
+ "name": "flag_ai",
+ "unicode": "1F1E6-1F1EE",
+ "digest": "75676ded736ad2ebb921e9fd8ebfef49819a35c3dcf005bbc3b7e8c6e75178f2"
+ },
+ {
+ "name": "flag_al",
+ "unicode": "1F1E6-1F1F1",
+ "digest": "77b835dcff399b609e2479cbf10f08344c8fc277370ba8e4540165ca15563847"
+ },
+ {
+ "name": "flag_am",
+ "unicode": "1F1E6-1F1F2",
+ "digest": "3b820c628dd5a93137f7288a43553778f60b0beea4c0a239d063893c0723e73d"
+ },
+ {
+ "name": "flag_ao",
+ "unicode": "1F1E6-1F1F4",
+ "digest": "d26439d4ecbe8b67bb1ae9753454505358ebb6b802624f19800471e53ee27187"
+ },
+ {
+ "name": "flag_aq",
+ "unicode": "1F1E6-1F1F6",
+ "digest": "6b0b4e800d88ab289ae4b6d449bfa115e92543958b477d13ad348468a74e4616"
+ },
+ {
+ "name": "flag_ar",
+ "unicode": "1F1E6-1F1F7",
+ "digest": "ca76db601dd3f5794f1caace8ab5641fe3786b86e4ae030706162f0ce07d27b3"
+ },
+ {
+ "name": "flag_as",
+ "unicode": "1F1E6-1F1F8",
+ "digest": "170e1dde0e3fd2e0f2149de5cc8845e15580cc0412e81a643d61bd387de16141"
+ },
+ {
+ "name": "flag_at",
+ "unicode": "1F1E6-1F1F9",
+ "digest": "0ab3675a16b4988e87c81e87453c160d6616c7be76247f54c471dc63aa8b42ba"
+ },
+ {
+ "name": "flag_au",
+ "unicode": "1F1E6-1F1FA",
+ "digest": "b6f17d3dfd3547c069a0b6cddd4cf44fb8ce1d1d300e24284fb292ac142537e3"
+ },
+ {
+ "name": "flag_aw",
+ "unicode": "1F1E6-1F1FC",
+ "digest": "7857bc907f04dfb7ccc4401c05034ad8afb6383a022db77973cfcafa4d6c16c8"
+ },
+ {
+ "name": "flag_ax",
+ "unicode": "1F1E6-1F1FD",
+ "digest": "ab8f1fd4af7c220a54d478cec5a9f7f3beb5fc83439c448f3ac9848af8391ac1"
+ },
+ {
+ "name": "flag_az",
+ "unicode": "1F1E6-1F1FF",
+ "digest": "187cc7b6d39800c5910a34409db1e6b1d8aac808c72a93e922a419d9b054fd0b"
+ },
+ {
+ "name": "flag_ba",
+ "unicode": "1F1E7-1F1E6",
+ "digest": "cd22c744213087384cf79ed314742026787212c9ceb6999ed166534670f7864a"
+ },
+ {
+ "name": "flag_bb",
+ "unicode": "1F1E7-1F1E7",
+ "digest": "44ff0a48ac2d2180374baa58b1b7c64f26d0d151a48811eb08ffa20758104512"
+ },
+ {
+ "name": "flag_bd",
+ "unicode": "1F1E7-1F1E9",
+ "digest": "c18793d2b963458607a0bab94c57e62c8278fce870e96fd8dda78067a8fbde18"
+ },
+ {
+ "name": "flag_be",
+ "unicode": "1F1E7-1F1EA",
+ "digest": "6e6ccfca064a43b93c8acc04a9425f95af204198022ca20b9ee6c491e99ad950"
+ },
+ {
+ "name": "flag_bf",
+ "unicode": "1F1E7-1F1EB",
+ "digest": "d69c0394a1c7cb6323f54f024b7d740c728f229ca5e1b54ac374d5024f5470a5"
+ },
+ {
+ "name": "flag_bg",
+ "unicode": "1F1E7-1F1EC",
+ "digest": "413a270caf4a9155e84bdba6c9512277f5642246f6ba8d701383a5eeb02f7e95"
+ },
+ {
+ "name": "flag_bh",
+ "unicode": "1F1E7-1F1ED",
+ "digest": "9243ed65d7f24c824c2a3207335a2d4ad25251258547c16d0b7b7cbb9df6f8de"
+ },
+ {
+ "name": "flag_bi",
+ "unicode": "1F1E7-1F1EE",
+ "digest": "63056519030524b2d2dcd47448267d817205dbd6b98075c97f011a8f1d4d1a4b"
+ },
+ {
+ "name": "flag_bj",
+ "unicode": "1F1E7-1F1EF",
+ "digest": "93b245eed85d22260d27d1a8c77f51fb3439309e09b2aeca6cd504dbea77b509"
+ },
+ {
+ "name": "flag_bl",
+ "unicode": "1F1E7-1F1F1",
+ "digest": "5e1e478deaf02bbaa26595e4cefc5f5c9bec6105ce521b7b9ab4fa5e7a452c14"
+ },
+ {
+ "name": "flag_black",
+ "unicode": "1F3F4",
+ "digest": "df131e5c28e9f51dea53fe7f33551f91d420f7d686b7a62980f0154c6b5357a6"
+ },
+ {
+ "name": "flag_bm",
+ "unicode": "1F1E7-1F1F2",
+ "digest": "9dcd9e60faebe7f93eb19157e99f2ad654a8145c61738de96e6ecd11a246764a"
+ },
+ {
+ "name": "flag_bn",
+ "unicode": "1F1E7-1F1F3",
+ "digest": "078af6ca481a77871ba005e251a46ce63951c27b1b0cd33b9c1d0d31d349bc1a"
+ },
+ {
+ "name": "flag_bo",
+ "unicode": "1F1E7-1F1F4",
+ "digest": "92516d04e922a3bcbabe2e7619194bc972c09ba97576e8155f9829c397a71d8c"
+ },
+ {
+ "name": "flag_bq",
+ "unicode": "1F1E7-1F1F6",
+ "digest": "7832df5267a2bb8dddb83aeb11162ce79aeebdb718f2ac0e54adcf3d87936171"
+ },
+ {
+ "name": "flag_br",
+ "unicode": "1F1E7-1F1F7",
+ "digest": "aabcc1c082124045ed214f7d9778d8e2ed791ebb8433defea91db458658abeec"
+ },
+ {
+ "name": "flag_bs",
+ "unicode": "1F1E7-1F1F8",
+ "digest": "f628f39003608e181696634929522884165e27ccef55270293f92eeef991635f"
+ },
+ {
+ "name": "flag_bt",
+ "unicode": "1F1E7-1F1F9",
+ "digest": "af24a8ab34815da04c3e5af49a47449e0de93b068957cbda695816d0f830ca12"
+ },
+ {
+ "name": "flag_bv",
+ "unicode": "1F1E7-1F1FB",
+ "digest": "ff0037f6eed95d4bb5f2b502902360e1ff41426e2896daf3e0730cef1f8f7e41"
+ },
+ {
+ "name": "flag_bw",
+ "unicode": "1F1E7-1F1FC",
+ "digest": "3e3241ecb97946cc3e467b083d113a57dd305595e1512d4da18cc403e8689c1d"
+ },
+ {
+ "name": "flag_by",
+ "unicode": "1F1E7-1F1FE",
+ "digest": "bdd21885c6fac475241884a44149b887297772e17617ee59dd9fe8518d52cf3d"
+ },
+ {
+ "name": "flag_bz",
+ "unicode": "1F1E7-1F1FF",
+ "digest": "21c16e1da641af004576000bf1db44b9a1e0fccfddc775e96022721c2f18eeea"
+ },
+ {
+ "name": "flag_ca",
+ "unicode": "1F1E8-1F1E6",
+ "digest": "0d00e459084d58d3ea9c60488a9e51bf45f71b77f1600f190225d5ca6ca6c796"
+ },
+ {
+ "name": "flag_cc",
+ "unicode": "1F1E8-1F1E8",
+ "digest": "86ab27164603ef0f1f83fe898eda6fbb7bc5709f2518f5577f00817860806a7b"
+ },
+ {
+ "name": "flag_cd",
+ "unicode": "1F1E8-1F1E9",
+ "digest": "fdc2796530ada4bd0bae37ace4bbe707b321b287dcd64568f8e01d3a9df56066"
+ },
+ {
+ "name": "flag_cf",
+ "unicode": "1F1E8-1F1EB",
+ "digest": "5943bec02bede0931e21e7c34a68f375499f60a34883cc1edf2f21e9834b15ce"
+ },
+ {
+ "name": "flag_cg",
+ "unicode": "1F1E8-1F1EC",
+ "digest": "54498482e2772371e148e05cfb7c5eb55f6a22cd528662abdea10bad47d157da"
+ },
+ {
+ "name": "flag_ch",
+ "unicode": "1F1E8-1F1ED",
+ "digest": "53d6d35aeeebb0b4b1ad858dc3691e649ac73d30b3be76f96d5fe9605fa99386"
+ },
+ {
+ "name": "flag_ci",
+ "unicode": "1F1E8-1F1EE",
+ "digest": "3a173a3058a5c0174dc88750852cafec264e901ce82a6c69db122c8c0ea71a3a"
+ },
+ {
+ "name": "flag_ck",
+ "unicode": "1F1E8-1F1F0",
+ "digest": "42f395ff53c618b72b8a224cd4343d1a32f5ad82ced56bf590170a5ff0d5134c"
+ },
+ {
+ "name": "flag_cl",
+ "unicode": "1F1E8-1F1F1",
+ "digest": "9d6255feb690596904d800e72d5acdb5cda941c5a741b031ea39a3c7650ac46f"
+ },
+ {
+ "name": "flag_cm",
+ "unicode": "1F1E8-1F1F2",
+ "digest": "ffc99d14e0a8b46a980331090ed9f36f31a87f1b0f8dd8c09007a31c6127c69e"
+ },
+ {
+ "name": "flag_cn",
+ "unicode": "1F1E8-1F1F3",
+ "digest": "869a98c52bdc33591f87e2aab6cb4f13e98bb19136250ff25805d0312a8b7c8a"
+ },
+ {
+ "name": "flag_co",
+ "unicode": "1F1E8-1F1F4",
+ "digest": "6aa458440eb2500ad307fea40fd8f1171a1506a6e32af144a4fd51545bb56151"
+ },
+ {
+ "name": "flag_cp",
+ "unicode": "1F1E8-1F1F5",
+ "digest": "62627702e3e3768808c12f153a527ffcc492ad74d8cdc1858cfde971efd0c8ee"
+ },
+ {
+ "name": "flag_cr",
+ "unicode": "1F1E8-1F1F7",
+ "digest": "0f3b54d8330c5bb136647547dafc598bda755697cfd6b7d872a2443ba7b5cad4"
+ },
+ {
+ "name": "flag_cu",
+ "unicode": "1F1E8-1F1FA",
+ "digest": "69bc973002475bb3d9b54cb0ba9ec9cb85f144c1cf54689da0ee8f414ebb0d83"
+ },
+ {
+ "name": "flag_cv",
+ "unicode": "1F1E8-1F1FB",
+ "digest": "af2e135cf3c1b03a5937c068a75061b5cd332e95902fd0f8dffb2ac2dc89692a"
+ },
+ {
+ "name": "flag_cw",
+ "unicode": "1F1E8-1F1FC",
+ "digest": "df4b2228a82f766c5c64c13c1388482a68549e59dd843671ee0eb43506e33411"
+ },
+ {
+ "name": "flag_cx",
+ "unicode": "1F1E8-1F1FD",
+ "digest": "db12e513345a7be53954167d359ede0b3effbfb292508ee4d726123e3a8f83d7"
+ },
+ {
+ "name": "flag_cy",
+ "unicode": "1F1E8-1F1FE",
+ "digest": "0cea41d4820746e2c6eb408f7ec7419afba9f7396401d92e6c1d77382f721d0b"
+ },
+ {
+ "name": "flag_cz",
+ "unicode": "1F1E8-1F1FF",
+ "digest": "a1c2405916963be306f761539123486a2845af53716c9dfe94ad5420e14d36c4"
+ },
+ {
+ "name": "flag_de",
+ "unicode": "1F1E9-1F1EA",
+ "digest": "74a80b64437bc4e31bdd7cbb753ecd2d719bf34c506cbac535db83a644174cce"
+ },
+ {
+ "name": "flag_dg",
+ "unicode": "1F1E9-1F1EC",
+ "digest": "13cb5ea872f94a9c3fb579cef417e2d1ed38e8cbe95059576380cacd59bc4b9d"
+ },
+ {
+ "name": "flag_dj",
+ "unicode": "1F1E9-1F1EF",
+ "digest": "5b479654c28d3eeb70055c5e25dc46ccaba9eeea7537cc45ca9dbb8186b743b6"
+ },
+ {
+ "name": "flag_dk",
+ "unicode": "1F1E9-1F1F0",
+ "digest": "dee7fa9644a9b447417518a353e7edcbb37b2af8bc7d13a6ed71d7210c43ca3c"
+ },
+ {
+ "name": "flag_dm",
+ "unicode": "1F1E9-1F1F2",
+ "digest": "2e339190a8a0a238140f42e329f6646af5be75763a787ea268488a2e0440dc4c"
+ },
+ {
+ "name": "flag_do",
+ "unicode": "1F1E9-1F1F4",
+ "digest": "be5dafcd32d7197a96d37299a91835a8009299452f05a66d91c5fdec17448230"
+ },
+ {
+ "name": "flag_dz",
+ "unicode": "1F1E9-1F1FF",
+ "digest": "cf525d56bac45fe689f92d441274fc0ecbed4f95591d2c066598f72b1ee8d618"
+ },
+ {
+ "name": "flag_ea",
+ "unicode": "1F1EA-1F1E6",
+ "digest": "1acb13950f7c3692f9a36e618d8ec10a73ead5d7fa80fb52b6b2a18e3d456002"
+ },
+ {
+ "name": "flag_ec",
+ "unicode": "1F1EA-1F1E8",
+ "digest": "4d9d35450efc6026651ccc2278e70fb90b001ca5e5eecd31361b1e4e23253dbd"
+ },
+ {
+ "name": "flag_ee",
+ "unicode": "1F1EA-1F1EA",
+ "digest": "86ec7b2f618fe71dddec3d5a621b56b878d683780f1e0ad446f965326d42df48"
+ },
+ {
+ "name": "flag_eg",
+ "unicode": "1F1EA-1F1EC",
+ "digest": "f06d36a6fec15af4c1a76de30e8469847dde2728bb5a48956b4e466098b778a4"
+ },
+ {
+ "name": "flag_eh",
+ "unicode": "1F1EA-1F1ED",
+ "digest": "eb63f5b92c62c98dc008dfa7ad8830aa17fa23964f812a28055bd8b6f5960c5b"
+ },
+ {
+ "name": "flag_er",
+ "unicode": "1F1EA-1F1F7",
+ "digest": "e901195f7b37b22a6872d36713de0ec176f6424c209e261e5c849ce318c772f6"
+ },
+ {
+ "name": "flag_es",
+ "unicode": "1F1EA-1F1F8",
+ "digest": "27ab5cc6c2e9f26ccdfa632887533eebcd9b514f80cec9e721cf8e5e2544339c"
+ },
+ {
+ "name": "flag_et",
+ "unicode": "1F1EA-1F1F9",
+ "digest": "6cdb3718c9b3ec713258dd36781db58b7da53f3017445056c1a76233e3b4a7de"
+ },
+ {
+ "name": "flag_eu",
+ "unicode": "1F1EA-1F1FA",
+ "digest": "363f60e8a747166d5cec8d70bfdf266411eec2ff07933b6187975075caadfd74"
+ },
+ {
+ "name": "flag_fi",
+ "unicode": "1F1EB-1F1EE",
+ "digest": "1a1959cb551a0e8bdaee8c04657fb7387a4d83173f7759f89468da12e1818a9e"
+ },
+ {
+ "name": "flag_fj",
+ "unicode": "1F1EB-1F1EF",
+ "digest": "f26dc36ea9c1f32d9bb54874ea384e7118b6e2585be69245fdd73acd8304ae78"
+ },
+ {
+ "name": "flag_fk",
+ "unicode": "1F1EB-1F1F0",
+ "digest": "0479e233499b704f91a9b13d083e66296efe2f28ed917ab1496b223bfb09adb8"
+ },
+ {
+ "name": "flag_fm",
+ "unicode": "1F1EB-1F1F2",
+ "digest": "142ea7b4b4a7004329925b495da43ab82351cbaac383c8da6e614b39ba58d05e"
+ },
+ {
+ "name": "flag_fo",
+ "unicode": "1F1EB-1F1F4",
+ "digest": "f1c800d4f4d39e2aead9a11ed500f16108d6bc48bd24bd2a1af7b966d8e76752"
+ },
+ {
+ "name": "flag_fr",
+ "unicode": "1F1EB-1F1F7",
+ "digest": "6f52f36b5199c65ab1cad13ff4e77d2d8b48a8ff79b92166976674ffdc7829ee"
+ },
+ {
+ "name": "flag_ga",
+ "unicode": "1F1EC-1F1E6",
+ "digest": "50a0d5a07466e419b74a4d532738f7958de9baa37df6191be4f3755dccc3b326"
+ },
+ {
+ "name": "flag_gb",
+ "unicode": "1F1EC-1F1E7",
+ "digest": "220f7da6d5a231b766c79f2e1b7d3fdb74ec0c0c17558cc00a8a8ccdf2afc2e0"
+ },
+ {
+ "name": "flag_gd",
+ "unicode": "1F1EC-1F1E9",
+ "digest": "3e162b0d13f4ceea7f663b1d425f13863d104e80df75a640f526e276bcd04081"
+ },
+ {
+ "name": "flag_ge",
+ "unicode": "1F1EC-1F1EA",
+ "digest": "35897f8254675d2efe9e3070c88af9ef214f08440e6ee75ebe81d28cdb57ea2b"
+ },
+ {
+ "name": "flag_gf",
+ "unicode": "1F1EC-1F1EB",
+ "digest": "3a34df321635f71a0f2cc4e1eda58d85c29230c77456362345196351bf56533d"
+ },
+ {
+ "name": "flag_gg",
+ "unicode": "1F1EC-1F1EC",
+ "digest": "c972f8d190b4e9ca8890df41503d202ffd73981833d3f3750f563302167bcd66"
+ },
+ {
+ "name": "flag_gh",
+ "unicode": "1F1EC-1F1ED",
+ "digest": "9c3d3569bd411389fa0af7c6938d4325cedeb9c0e8f059dc1d5a74c6b8d6d01b"
+ },
+ {
+ "name": "flag_gi",
+ "unicode": "1F1EC-1F1EE",
+ "digest": "ede638bc6fedc30a01821025d87ec19297500da9c04a7a155984fca186118649"
+ },
+ {
+ "name": "flag_gl",
+ "unicode": "1F1EC-1F1F1",
+ "digest": "a2ce3371eff1da8331671925f707232aa593ac7400d59555c9ca689729ce24ec"
+ },
+ {
+ "name": "flag_gm",
+ "unicode": "1F1EC-1F1F2",
+ "digest": "932bf6eb75ddd4278268dd2f09d8fffcfef89f8fd6b6e86a08a414cd3ceec94d"
+ },
+ {
+ "name": "flag_gn",
+ "unicode": "1F1EC-1F1F3",
+ "digest": "ebf543713895adaa09d64897f24bd461191191b8fcbbcede52bdaf4bd2dc67a8"
+ },
+ {
+ "name": "flag_gp",
+ "unicode": "1F1EC-1F1F5",
+ "digest": "2e6c48d80c571b34f31fa9b3622dcc51e1707c0118e991e9c177742ff02a8a96"
+ },
+ {
+ "name": "flag_gq",
+ "unicode": "1F1EC-1F1F6",
+ "digest": "b0f5810180d12fc48faf75e73f882dc59072d7bf957f8455bf7e1e336539dc41"
+ },
+ {
+ "name": "flag_gr",
+ "unicode": "1F1EC-1F1F7",
+ "digest": "8d60d6f8910f5179d851dbea0798b56a492c6be85f3d55e1a1126cd1d6663a3b"
+ },
+ {
+ "name": "flag_gs",
+ "unicode": "1F1EC-1F1F8",
+ "digest": "7b07915af0e2364ebc386a162d44846f3a7986fdd24e20ad2bc56d64a103fe9c"
+ },
+ {
+ "name": "flag_gt",
+ "unicode": "1F1EC-1F1F9",
+ "digest": "0c78108ede45bf34917b409a0867f5ec8253c74b694beda083f3e8d04d7a10d8"
+ },
+ {
+ "name": "flag_gu",
+ "unicode": "1F1EC-1F1FA",
+ "digest": "909f1bc98fa1507adb787eb3875503b21ea937d6ae8bb152153916c2da5e13bb"
+ },
+ {
+ "name": "flag_gw",
+ "unicode": "1F1EC-1F1FC",
+ "digest": "f5f34410c7b22d5ed9994b47d0e7a9d9a6a1f05c4d3142f7fef3e4409725f5e6"
+ },
+ {
+ "name": "flag_gy",
+ "unicode": "1F1EC-1F1FE",
+ "digest": "4939cf52ab34a924a31032b42668960a2c7d8d4f998b16b065c247110df334be"
+ },
+ {
+ "name": "flag_hk",
+ "unicode": "1F1ED-1F1F0",
+ "digest": "bde0916df6d62f6b1cf8f85a8a39526c97fc6ef6fedb0b0cae2adb127a08eafe"
+ },
+ {
+ "name": "flag_hm",
+ "unicode": "1F1ED-1F1F2",
+ "digest": "603e6c9bff9a0dc941970a313fe98fbf53ff5a57028f1a2766420be4211711cc"
+ },
+ {
+ "name": "flag_hn",
+ "unicode": "1F1ED-1F1F3",
+ "digest": "2953ad0909bc32c02615f6ad5a4e5f331ba794a41632b1f0fc366e1c640cc2b9"
+ },
+ {
+ "name": "flag_hr",
+ "unicode": "1F1ED-1F1F7",
+ "digest": "41c9ffc4f0faaa2d77e5cffb781329e7d2489ce879bd8eb9c503621e834abc50"
+ },
+ {
+ "name": "flag_ht",
+ "unicode": "1F1ED-1F1F9",
+ "digest": "6a56c3d71b4f858e1774aa2134a9f5584087fec968e9ee8bb1046d2ec93bf059"
+ },
+ {
+ "name": "flag_hu",
+ "unicode": "1F1ED-1F1FA",
+ "digest": "72f5809818d4cab8c0cee73df7f67b820fb8471eea4199911a5917ac099795e8"
+ },
+ {
+ "name": "flag_ic",
+ "unicode": "1F1EE-1F1E8",
+ "digest": "7e2a7667fcd05f927af47e64c5790c104a9956dd9f1a45f03cb0fdcc85d866d3"
+ },
+ {
+ "name": "flag_id",
+ "unicode": "1F1EE-1F1E9",
+ "digest": "4721f616fae2e443e52f1e9cc96e4835bddca16a2d75d7d5afea57cdee866b7f"
+ },
+ {
+ "name": "flag_ie",
+ "unicode": "1F1EE-1F1EA",
+ "digest": "84b19833e6c9fb43187f8a28d85045a3df58816f20a07edab90474323174b1f3"
+ },
+ {
+ "name": "flag_il",
+ "unicode": "1F1EE-1F1F1",
+ "digest": "c99d4bd8c2541cf3a7392c4faf4477d96bc47065dd1423b9e06450483e69b34f"
+ },
+ {
+ "name": "flag_im",
+ "unicode": "1F1EE-1F1F2",
+ "digest": "5eeb12c0315b527ce61649a38b64d76af726a73b2d381d1a1ddd1366bafb1bfc"
+ },
+ {
+ "name": "flag_in",
+ "unicode": "1F1EE-1F1F3",
+ "digest": "ecc3cfcff3368fe0875a51a8be9f4dfd449a187e5beb41a2b34241736247f73b"
+ },
+ {
+ "name": "flag_io",
+ "unicode": "1F1EE-1F1F4",
+ "digest": "26243d60e04ba3bc9eb8f008bfc77b2a64bcf1a3d0073eb0449a8c8121618c9c"
+ },
+ {
+ "name": "flag_iq",
+ "unicode": "1F1EE-1F1F6",
+ "digest": "a1fb5e59575081920b3be5290f654d57a9be099deb56d4ed69eba81a2b531cb3"
+ },
+ {
+ "name": "flag_ir",
+ "unicode": "1F1EE-1F1F7",
+ "digest": "ab89488b934af1d4bdae7ed16dfc74fffe658bb8e95d5161b48cdd06de44ae85"
+ },
+ {
+ "name": "flag_is",
+ "unicode": "1F1EE-1F1F8",
+ "digest": "55db1fc9e6c56d4c9bcb9a46e5e4300cf2a0c32fa91dc24b487a1d56c8097268"
+ },
+ {
+ "name": "flag_it",
+ "unicode": "1F1EE-1F1F9",
+ "digest": "36fc993fb00ab607578a4d0e573e988e17b9459a68a000a48de905a8238589d0"
+ },
+ {
+ "name": "flag_je",
+ "unicode": "1F1EF-1F1EA",
+ "digest": "c608dbfd1259330e2f8c40dc5d12ffd0489396f4fc5f3ca57bcb2f0d9d05c20c"
+ },
+ {
+ "name": "flag_jm",
+ "unicode": "1F1EF-1F1F2",
+ "digest": "a8224b68b2d324f848d75e4376875ef76a8174e6ba32790d9ca622fe1eabfd5f"
+ },
+ {
+ "name": "flag_jo",
+ "unicode": "1F1EF-1F1F4",
+ "digest": "2403563dc2ab4ed0e7e3a0761cc09f96801550bba6b177b54d651d8804ad987d"
+ },
+ {
+ "name": "flag_jp",
+ "unicode": "1F1EF-1F1F5",
+ "digest": "aea8eebd0a0139818cb7629d9c9a8e55160b458eb8ffeee2f36c5cff4b507fd3"
+ },
+ {
+ "name": "flag_ke",
+ "unicode": "1F1F0-1F1EA",
+ "digest": "9c8365f74858743bcdce4a9cf6a6f4110faf2dc6433e5dc7d98c24bb3b32a36d"
+ },
+ {
+ "name": "flag_kg",
+ "unicode": "1F1F0-1F1EC",
+ "digest": "0c72bdb1d64b1e3be3d9516a50655a6162d8501851d2cf2fadb8c6ef7740df4e"
+ },
+ {
+ "name": "flag_kh",
+ "unicode": "1F1F0-1F1ED",
+ "digest": "49e41e488732d789e395091e144cd6215c6818ba2073e5e22ea21203a737d03c"
+ },
+ {
+ "name": "flag_ki",
+ "unicode": "1F1F0-1F1EE",
+ "digest": "9d7f168adbcf5f4cfe28470addfdb0a8b231438d593edb70f633981bfa4c7638"
+ },
+ {
+ "name": "flag_km",
+ "unicode": "1F1F0-1F1F2",
+ "digest": "9318c28957fa7a19eba5ec452c1cbce01a5a83d41d29d081614d3abb0585d478"
+ },
+ {
+ "name": "flag_kn",
+ "unicode": "1F1F0-1F1F3",
+ "digest": "eac7e7d0f023dee5c0c8559bc2c9a96273adda54ce47598025120b30d8d6ebc1"
+ },
+ {
+ "name": "flag_kp",
+ "unicode": "1F1F0-1F1F5",
+ "digest": "d4d53db6f8363174de6db864c056267ba8a7d7e87b5527f2f42bb9b8ac3f362b"
+ },
+ {
+ "name": "flag_kr",
+ "unicode": "1F1F0-1F1F7",
+ "digest": "5c7e61ab4a2aae70cbe51f0ca4718516002bc943b35d870bd853a0c98c4e0ed5"
+ },
+ {
+ "name": "flag_kw",
+ "unicode": "1F1F0-1F1FC",
+ "digest": "5d229cd99d25f4285bd30d98cfcc3cd8346648897476e2905a1811ceeef48d37"
+ },
+ {
+ "name": "flag_ky",
+ "unicode": "1F1F0-1F1FE",
+ "digest": "9ce3d8dfc273d3a400960876c434b702f93df92c6c00682dbed2ec8e3966d8a8"
+ },
+ {
+ "name": "flag_kz",
+ "unicode": "1F1F0-1F1FF",
+ "digest": "a6f0be0a767fa4824495d568d9fc2bd8d4c1a26f363873d3b65362e9383e2a50"
+ },
+ {
+ "name": "flag_la",
+ "unicode": "1F1F1-1F1E6",
+ "digest": "ab2ae96da87f7b53ab212f8dcd897a591cff9ea6666270097a8e739ee0b8f8cb"
+ },
+ {
+ "name": "flag_lb",
+ "unicode": "1F1F1-1F1E7",
+ "digest": "0c3fcab22e9fae1c78658290aff97de785d0b6adb5e3702d00073ce774b7ed54"
+ },
+ {
+ "name": "flag_lc",
+ "unicode": "1F1F1-1F1E8",
+ "digest": "e154b0b3a1635a36e0d9ad518c0ea12259320e5f1ebbda982248486492065d28"
+ },
+ {
+ "name": "flag_li",
+ "unicode": "1F1F1-1F1EE",
+ "digest": "bbc393a89e73cc8c29a0a9297428d07aa1d4717ea9b7d4dd9d69f21ac7d0605d"
+ },
+ {
+ "name": "flag_lk",
+ "unicode": "1F1F1-1F1F0",
+ "digest": "376bd501d113a844971ca1006ab31aa086cd55d74842ea5f3dedaba997b58693"
+ },
+ {
+ "name": "flag_lr",
+ "unicode": "1F1F1-1F1F7",
+ "digest": "9a6ebe1c9d9a53079ee77292a5ad0965f96409b0417f92876a1c3bd463d6a9bc"
+ },
+ {
+ "name": "flag_ls",
+ "unicode": "1F1F1-1F1F8",
+ "digest": "e2f4b05414f6e0c3d629a92b0534d4145475f0214a83a62c902fe0884c833c89"
+ },
+ {
+ "name": "flag_lt",
+ "unicode": "1F1F1-1F1F9",
+ "digest": "d5e2f8b2ffa820a33ea6d612fccd61e32467d25154342f5be134d3520e48387f"
+ },
+ {
+ "name": "flag_lu",
+ "unicode": "1F1F1-1F1FA",
+ "digest": "f43277103292195b51981d08e2dde68eab660a65c7875f510e09a8b2370f1b5c"
+ },
+ {
+ "name": "flag_lv",
+ "unicode": "1F1F1-1F1FB",
+ "digest": "e1288ac5c80d6e9d577d652e34be247ca39bf9d3d7cfc8a6cae13c1f9ac9dc47"
+ },
+ {
+ "name": "flag_ly",
+ "unicode": "1F1F1-1F1FE",
+ "digest": "5122294b769a174e3b6e3d238bb846b3e760929f5bb3c1a708d8a429f3f32f68"
+ },
+ {
+ "name": "flag_ma",
+ "unicode": "1F1F2-1F1E6",
+ "digest": "615a6447ff284de7689b4fd7b04fdda308f65dbbec958cfb96d2977514981d16"
+ },
+ {
+ "name": "flag_mc",
+ "unicode": "1F1F2-1F1E8",
+ "digest": "08b48b28938acbfc0fbc15c25ee14dbad7164c5165d03df2eee370755ee7b4cf"
+ },
+ {
+ "name": "flag_md",
+ "unicode": "1F1F2-1F1E9",
+ "digest": "93d61de68f821e1e08b30e63d91e8b4a657766475128538894cf9da9a3b4e3c0"
+ },
+ {
+ "name": "flag_me",
+ "unicode": "1F1F2-1F1EA",
+ "digest": "ee55c0eb78241aec2baf1822a47fa46d63209ceae3db7617ae886b823ae229ff"
+ },
+ {
+ "name": "flag_mf",
+ "unicode": "1F1F2-1F1EB",
+ "digest": "62627702e3e3768808c12f153a527ffcc492ad74d8cdc1858cfde971efd0c8ee"
+ },
+ {
+ "name": "flag_mg",
+ "unicode": "1F1F2-1F1EC",
+ "digest": "86ec8140e2c4854f52cff74757baf0cbb75a4aacca8be6af8c8f9c939a7b866c"
+ },
+ {
+ "name": "flag_mh",
+ "unicode": "1F1F2-1F1ED",
+ "digest": "8311ea3422c9d5e94b55e19b03bedd6fe6e2a191b7657e15ac75a48932958a5b"
+ },
+ {
+ "name": "flag_mk",
+ "unicode": "1F1F2-1F1F0",
+ "digest": "5c6f504f88c5a875c06ac8b26fa6e81a9d79c42a1c7d1fad9a5d4c8ad06ca502"
+ },
+ {
+ "name": "flag_ml",
+ "unicode": "1F1F2-1F1F1",
+ "digest": "d08a4973db40cf28e58ca3c80e8bd4e50d68ba1080b31917aeefdb0e210b5c50"
+ },
+ {
+ "name": "flag_mm",
+ "unicode": "1F1F2-1F1F2",
+ "digest": "5e95089514ca09bb93afb481b317477c9d053adcf450e0b711d78ed1078c7470"
+ },
+ {
+ "name": "flag_mn",
+ "unicode": "1F1F2-1F1F3",
+ "digest": "7a0ca72715dd2a36eeeed2f8c888497cb752f0000af8f07d6930743caf6e4273"
+ },
+ {
+ "name": "flag_mo",
+ "unicode": "1F1F2-1F1F4",
+ "digest": "d2c7c2191bc1bc83d85f2270968cb4de5cf26a11f70e166a8b32c108287ef729"
+ },
+ {
+ "name": "flag_mp",
+ "unicode": "1F1F2-1F1F5",
+ "digest": "89ad06121fd7981338fe188464491bea371f85125bfb4fc01fb5cad606613b1e"
+ },
+ {
+ "name": "flag_mq",
+ "unicode": "1F1F2-1F1F6",
+ "digest": "98176f3af823b26a3657a17c5073ee22367898b40bd3973de76329aa87ca5a2e"
+ },
+ {
+ "name": "flag_mr",
+ "unicode": "1F1F2-1F1F7",
+ "digest": "cc3e705ad84f83fe2d544385c39564743024dab26595d62469b35fdb791f6015"
+ },
+ {
+ "name": "flag_ms",
+ "unicode": "1F1F2-1F1F8",
+ "digest": "465e3d5700b557f2589bd6e34a0c6b12c634a6ed4dcfbee3c1c841c5de3413f0"
+ },
+ {
+ "name": "flag_mt",
+ "unicode": "1F1F2-1F1F9",
+ "digest": "e610ba22d8d8ad750ed10dff8e1b4d89bc34f066c3424bfa77dbdc1a5d79743a"
+ },
+ {
+ "name": "flag_mu",
+ "unicode": "1F1F2-1F1FA",
+ "digest": "3daf015d3b95218677dafbb282b7804686aa68875a6bd1d70c165b7b149e19cb"
+ },
+ {
+ "name": "flag_mv",
+ "unicode": "1F1F2-1F1FB",
+ "digest": "d30e4bfd04f08177de92f3c175600aaafa89b9668bbe2b83f35f07a74382065c"
+ },
+ {
+ "name": "flag_mw",
+ "unicode": "1F1F2-1F1FC",
+ "digest": "f364b1c8bfda3f86b5e26422eedc571ba11e312dcc634197631a6840cb22aede"
+ },
+ {
+ "name": "flag_mx",
+ "unicode": "1F1F2-1F1FD",
+ "digest": "eafb02ec0be9cefab7cef7c426c7d860d98e4947f4da04054154dc86d8f487c4"
+ },
+ {
+ "name": "flag_my",
+ "unicode": "1F1F2-1F1FE",
+ "digest": "9a690b357bc6b970781bd122c1e546ade3ccb73d930c2af1008b82027e36c7cf"
+ },
+ {
+ "name": "flag_mz",
+ "unicode": "1F1F2-1F1FF",
+ "digest": "36d0548ebfef9e0443ec1d0597ebfa6e95c25b997381f30c8c74008820743bb9"
+ },
+ {
+ "name": "flag_na",
+ "unicode": "1F1F3-1F1E6",
+ "digest": "4989dc9452b0bdfa101cfd3b7c83ef1195a7e45128b9ed00193fe712a6d02fca"
+ },
+ {
+ "name": "flag_nc",
+ "unicode": "1F1F3-1F1E8",
+ "digest": "7fc9d865eebf729d5496c4cd7576476ec599f65b379d4a6df66b4e399553c2eb"
+ },
+ {
+ "name": "flag_ne",
+ "unicode": "1F1F3-1F1EA",
+ "digest": "d3f10fb44ec44a04112bc66d05f0a44c6ec46dae73cfd3fe26cdc8b32ec06713"
+ },
+ {
+ "name": "flag_nf",
+ "unicode": "1F1F3-1F1EB",
+ "digest": "d390e0d52215a025380af221ba9e955e5886edbb4c9f4b124f2fb60a8e019e42"
+ },
+ {
+ "name": "flag_ng",
+ "unicode": "1F1F3-1F1EC",
+ "digest": "e69d1bb8f1db4a0c295c90dda23d8f97c2dea59f9a2da2ecb0e9a1dc4dbea101"
+ },
+ {
+ "name": "flag_ni",
+ "unicode": "1F1F3-1F1EE",
+ "digest": "dbaccc942637469b0ee75bd5f956958c3c5a89d8f69b69c96f02ab6594124894"
+ },
+ {
+ "name": "flag_nl",
+ "unicode": "1F1F3-1F1F1",
+ "digest": "bda2eb0315763c3c19d37c664dab1ee4280f20888a0ca57677fd33cfa4240910"
+ },
+ {
+ "name": "flag_no",
+ "unicode": "1F1F3-1F1F4",
+ "digest": "42b49dec756a220781ea271ca8fbcaba524dc3b38d5d8f999bfaa40ef9ebd302"
+ },
+ {
+ "name": "flag_np",
+ "unicode": "1F1F3-1F1F5",
+ "digest": "b5259257db079235310d5d9537d2b5b61ae0326bc8920ba13084b009844e2957"
+ },
+ {
+ "name": "flag_nr",
+ "unicode": "1F1F3-1F1F7",
+ "digest": "1bd7d1fe2c3a5e98cfd4dff6e8d6dd6d3c74f0051ad615587d77d2291a9784cc"
+ },
+ {
+ "name": "flag_nu",
+ "unicode": "1F1F3-1F1FA",
+ "digest": "e2a7a398e07d2232147cc0917d72d18b519246d3d314e9f6f03dcf98d312d4ce"
+ },
+ {
+ "name": "flag_nz",
+ "unicode": "1F1F3-1F1FF",
+ "digest": "ce8b1cb87dae3a3ec865575b57a0b4987a7f4bd3f170e7b210dd764fc2588cd4"
+ },
+ {
+ "name": "flag_om",
+ "unicode": "1F1F4-1F1F2",
+ "digest": "29da72505a276a8a372a00c197388ebc5098c221cab26b3ff755bd62b10f740f"
+ },
+ {
+ "name": "flag_pa",
+ "unicode": "1F1F5-1F1E6",
+ "digest": "180b673c9aceea43a8b55823a82d80600257e4982d0757d129860e3d8a14f458"
+ },
+ {
+ "name": "flag_pe",
+ "unicode": "1F1F5-1F1EA",
+ "digest": "b61823ea2cd91e371e40832df5764558b81d44fac41030827a3f6d2564643c00"
+ },
+ {
+ "name": "flag_pf",
+ "unicode": "1F1F5-1F1EB",
+ "digest": "e560421911f4af90c73a0dbdf8f42e69316003799304c9394fb127e3b83326fa"
+ },
+ {
+ "name": "flag_pg",
+ "unicode": "1F1F5-1F1EC",
+ "digest": "880e87db2ce0eac38db037683a5db46fd6ce30623cf56ae4a93a747103570044"
+ },
+ {
+ "name": "flag_ph",
+ "unicode": "1F1F5-1F1ED",
+ "digest": "49aae2f56bfd1385741dc76857aa1f1459778b2d39a1c955e469c5367585bfd5"
+ },
+ {
+ "name": "flag_pk",
+ "unicode": "1F1F5-1F1F0",
+ "digest": "64379dbfc932df3a07935b5cfa11ca151f761d3728939e982604e12c663cd646"
+ },
+ {
+ "name": "flag_pl",
+ "unicode": "1F1F5-1F1F1",
+ "digest": "3b688b074c2735d3dea0b7ab74b80eba243ce50cb05d68e585c9d701c1f14617"
+ },
+ {
+ "name": "flag_pm",
+ "unicode": "1F1F5-1F1F2",
+ "digest": "a13a69ee3131501dd8138173cfb669a35ee8039d84aa665e69dd7f0d0aa3e717"
+ },
+ {
+ "name": "flag_pn",
+ "unicode": "1F1F5-1F1F3",
+ "digest": "d7ae3985cf66024e4a3001e79a8efbb3e75571f2b0abbd0fb87fc1efc795a2b3"
+ },
+ {
+ "name": "flag_pr",
+ "unicode": "1F1F5-1F1F7",
+ "digest": "4910dc984bc908158506b770f28af56150cbb4509a4291947dfa2479b9e4b308"
+ },
+ {
+ "name": "flag_ps",
+ "unicode": "1F1F5-1F1F8",
+ "digest": "b2bca7619fced25de94d7bd398537857460348a552e7d73d189aef3f428e6a13"
+ },
+ {
+ "name": "flag_pt",
+ "unicode": "1F1F5-1F1F9",
+ "digest": "177282613b4b8b4d9551f1da6a1c3f66f1b96cf67c71c7d164213b26b3237395"
+ },
+ {
+ "name": "flag_pw",
+ "unicode": "1F1F5-1F1FC",
+ "digest": "2ff42a14bdc7df76b5f989dca381f94765032b26ae47d47b97844abde458cefe"
+ },
+ {
+ "name": "flag_py",
+ "unicode": "1F1F5-1F1FE",
+ "digest": "80169b69a46c4c67d0090dc2c6bf05d1a14f133ac7ae56f811547e8e8f70d81b"
+ },
+ {
+ "name": "flag_qa",
+ "unicode": "1F1F6-1F1E6",
+ "digest": "589b44b975aa97426afb8db7f8b355491fca246b693903485824bf0f5a6953a2"
+ },
+ {
+ "name": "flag_re",
+ "unicode": "1F1F7-1F1EA",
+ "digest": "77d242261742831a142c9ec74cd17d76b1e6d1af751ff3c6a356646744bc798a"
+ },
+ {
+ "name": "flag_ro",
+ "unicode": "1F1F7-1F1F4",
+ "digest": "d7d17026ea81f27456983722540f9a23343a3a1b22e7697c4fba118ce8b4719e"
+ },
+ {
+ "name": "flag_rs",
+ "unicode": "1F1F7-1F1F8",
+ "digest": "e466a18cc0368e623d3fe33a036c1e88db91ae24f7510e17caacc85c41f1bac8"
+ },
+ {
+ "name": "flag_ru",
+ "unicode": "1F1F7-1F1FA",
+ "digest": "86bf53a62dfc4c434d910f43df70f430fc67c0070fe3fc466c4fbfd6a5d8e646"
+ },
+ {
+ "name": "flag_rw",
+ "unicode": "1F1F7-1F1FC",
+ "digest": "38ec5a01896c9747a8dbf865d5e8584770e587253b7af3d3b9c36cd993f67518"
+ },
+ {
+ "name": "flag_sa",
+ "unicode": "1F1F8-1F1E6",
+ "digest": "a44d0b145f2a0b68eace24ecfd27519e9525ec764836728bc9c1fe96ccb811a0"
+ },
+ {
+ "name": "flag_sb",
+ "unicode": "1F1F8-1F1E7",
+ "digest": "8ffa24c5cb92be4dbe43f6cd85b61b9608a3101bd78ebccff4fe99c209b3e241"
+ },
+ {
+ "name": "flag_sc",
+ "unicode": "1F1F8-1F1E8",
+ "digest": "227d090ac2cbf317e594567b6114b5063a13cfe33abf990d37b200debcfadabb"
+ },
+ {
+ "name": "flag_sd",
+ "unicode": "1F1F8-1F1E9",
+ "digest": "350f3332e8ea1138e54facc870dd0fea5f2ab7d3fd4baa02ed8627ae79642f6c"
+ },
+ {
+ "name": "flag_se",
+ "unicode": "1F1F8-1F1EA",
+ "digest": "c1b09f36c263727de83b54376f05e083a17a61941af9a1640b826629256a280d"
+ },
+ {
+ "name": "flag_sg",
+ "unicode": "1F1F8-1F1EC",
+ "digest": "e6fc26920dfc07e4fd3c8d897de9c607e0bf48a3b64a13630c858d707a8e7660"
+ },
+ {
+ "name": "flag_sh",
+ "unicode": "1F1F8-1F1ED",
+ "digest": "f2c22ab0eb49e3104c35f1c0268b1e63c3a67f41b0cfa9861b189525988e53b6"
+ },
+ {
+ "name": "flag_si",
+ "unicode": "1F1F8-1F1EE",
+ "digest": "1ef0b10e498f71591322f9d8ec122d39838f479370cf7ee922560986ef6c4f2e"
+ },
+ {
+ "name": "flag_sj",
+ "unicode": "1F1F8-1F1EF",
+ "digest": "ce913b007f84a9cba2add8d754aa791901624c60e4200de426dfa25271cb0f78"
+ },
+ {
+ "name": "flag_sk",
+ "unicode": "1F1F8-1F1F0",
+ "digest": "d8f8fc4024c82f906effe98facbef9d543fb3708b1134dc502c74dc4a442b30a"
+ },
+ {
+ "name": "flag_sl",
+ "unicode": "1F1F8-1F1F1",
+ "digest": "dd7fd0452498d8d1c894cf0d5a662ddff9c5bcc02148bdc3dc7e6f25d0bb586e"
+ },
+ {
+ "name": "flag_sm",
+ "unicode": "1F1F8-1F1F2",
+ "digest": "2b499606aee2b5cbf4037338753c80a4c8f75f4abcef2c8657bd9337e602bbd3"
+ },
+ {
+ "name": "flag_sn",
+ "unicode": "1F1F8-1F1F3",
+ "digest": "03b46a9d8b129da13f60c23b820b04fba52050ca58a41b859ad57d5c3cc2515d"
+ },
+ {
+ "name": "flag_so",
+ "unicode": "1F1F8-1F1F4",
+ "digest": "ea416b6a05ddc5b16291ebe5101735360b08c834d55ac82c663ac1dd3e459048"
+ },
+ {
+ "name": "flag_sr",
+ "unicode": "1F1F8-1F1F7",
+ "digest": "012179fbcbcb7343e7b09d33e283fb63c7964a6eca35ccb9407d468e495a9874"
+ },
+ {
+ "name": "flag_ss",
+ "unicode": "1F1F8-1F1F8",
+ "digest": "6723150482c640643c9dd7e33ea749f4a8b46aceacbd4f5e11aa33b3ee13aab7"
+ },
+ {
+ "name": "flag_st",
+ "unicode": "1F1F8-1F1F9",
+ "digest": "0947fcec2e3cb1b0e9943c3d00891e8ee226e8d0532e9b1fe807ddf2e8fbc49d"
+ },
+ {
+ "name": "flag_sv",
+ "unicode": "1F1F8-1F1FB",
+ "digest": "ce7e583db833c4b10e2f7a2d09b97bb522c02e96ea0b3f3a48a955f7d8f970d8"
+ },
+ {
+ "name": "flag_sx",
+ "unicode": "1F1F8-1F1FD",
+ "digest": "c01fb238c7ba439f24a5ef821b6457f2a0fd0b99a1b2d02395bed87f0a4a88e5"
+ },
+ {
+ "name": "flag_sy",
+ "unicode": "1F1F8-1F1FE",
+ "digest": "a77d87ef98c96140c59998d10d94837e2a056dd3ac5c7522e89e5c62eac69e69"
+ },
+ {
+ "name": "flag_sz",
+ "unicode": "1F1F8-1F1FF",
+ "digest": "2904ad01040a9107ad556ec4c2561781d96746005cca250babb1127b8ba21050"
+ },
+ {
+ "name": "flag_ta",
+ "unicode": "1F1F9-1F1E6",
+ "digest": "eda84db90e1a8854e8ff3c15b3b38ee65f7d6532b76970a6fbac304c30d8c959"
+ },
+ {
+ "name": "flag_tc",
+ "unicode": "1F1F9-1F1E8",
+ "digest": "4628fdf6dc598a2846beefe97f7d4c6812f4961394cec132924b44bbe79b3322"
+ },
+ {
+ "name": "flag_td",
+ "unicode": "1F1F9-1F1E9",
+ "digest": "125ff31e4285cb2a5493a52a2703ebe8e7138b918ec4dae3d0f8693632372df6"
+ },
+ {
+ "name": "flag_tf",
+ "unicode": "1F1F9-1F1EB",
+ "digest": "489d591e11764ac341f2234020f7879db782b8f673fc9aae425fd713e4082334"
+ },
+ {
+ "name": "flag_tg",
+ "unicode": "1F1F9-1F1EC",
+ "digest": "4ceedfcfcc22cd14d9add9d86d6748447995f19f7095fa4be883e21eb1aa86bc"
+ },
+ {
+ "name": "flag_th",
+ "unicode": "1F1F9-1F1ED",
+ "digest": "2798cc660af1c5dc4891c30aded3a53d7cfa0af128cc495df8141907b165902d"
+ },
+ {
+ "name": "flag_tj",
+ "unicode": "1F1F9-1F1EF",
+ "digest": "0483506fc5b5f2d4fc18ea3cd2f8a5da985d68fe4bf90bd3fd05e67e38f32398"
+ },
+ {
+ "name": "flag_tk",
+ "unicode": "1F1F9-1F1F0",
+ "digest": "d5d4a8c6ce3207731b7c154a9d8d8fa2af055a48f03b3cbbcfd3317d3b8a75f2"
+ },
+ {
+ "name": "flag_tl",
+ "unicode": "1F1F9-1F1F1",
+ "digest": "7a2ba8f91a6b627c60c88244223a9b9d0c12707f50b174f9c2eca07dd3440df7"
+ },
+ {
+ "name": "flag_tm",
+ "unicode": "1F1F9-1F1F2",
+ "digest": "adcf5f23adcf983ce626b44559482f8728251eab34b3ff5d8b125112f3a1010f"
+ },
+ {
+ "name": "flag_tn",
+ "unicode": "1F1F9-1F1F3",
+ "digest": "5ee690ee1f3c3c0cba9b36efdef902894ec59cefbc60c4baa341efd3d7bb9ba2"
+ },
+ {
+ "name": "flag_to",
+ "unicode": "1F1F9-1F1F4",
+ "digest": "cde8672ca25b0e3a423865283fab9bc3ab10f472e04979b3b2f8032b71e96300"
+ },
+ {
+ "name": "flag_tr",
+ "unicode": "1F1F9-1F1F7",
+ "digest": "3d83c03ed084cfc81fa633310382acd7213e1eaa19d0ed97d142e7824032b55d"
+ },
+ {
+ "name": "flag_tt",
+ "unicode": "1F1F9-1F1F9",
+ "digest": "d66d272ac27e2b398289d6b60128ccd3508aeb1f4a00a3920c5e6a21bfe357ed"
+ },
+ {
+ "name": "flag_tv",
+ "unicode": "1F1F9-1F1FB",
+ "digest": "8716527383854cf1569f737d0f0f9ad77b46747255f24e02f5b2fbc850c2e35c"
+ },
+ {
+ "name": "flag_tw",
+ "unicode": "1F1F9-1F1FC",
+ "digest": "fb17b97e18e4423c5f60d60ec3ec60b917be579fc4dd9b5b23236786dcb35108"
+ },
+ {
+ "name": "flag_tz",
+ "unicode": "1F1F9-1F1FF",
+ "digest": "a8a8cf57ae5227cb54620bf31d2d6e154d2067d6d049b8db64bc4e538222948b"
+ },
+ {
+ "name": "flag_ua",
+ "unicode": "1F1FA-1F1E6",
+ "digest": "03aca4b3ffd60d944a5793eb7530f8d8ae527782f642f6606194e46ee314b12c"
+ },
+ {
+ "name": "flag_ug",
+ "unicode": "1F1FA-1F1EC",
+ "digest": "70226a1585e88390b3b815b8b79a0ddb36d2961c6b465c4ff72aa444abfe982e"
+ },
+ {
+ "name": "flag_um",
+ "unicode": "1F1FA-1F1F2",
+ "digest": "aa83bf051149acf907140a860de5de1700710e4164ae5549ad1040b24d0a142b"
+ },
+ {
+ "name": "flag_us",
+ "unicode": "1F1FA-1F1F8",
+ "digest": "32ba2aa09a30514247e91d60762791b582f547a37d9151f98b700dff50f355ea"
+ },
+ {
+ "name": "flag_uy",
+ "unicode": "1F1FA-1F1FE",
+ "digest": "0e01b3f1df4bdf6d616dacc9c5825151b941bf074be750e8b24a07ea5d5bcacb"
+ },
+ {
+ "name": "flag_uz",
+ "unicode": "1F1FA-1F1FF",
+ "digest": "903029ce83812a2134f24b65db35b183443a440ea5fecaa6ef7dcaaf65b2519c"
+ },
+ {
+ "name": "flag_va",
+ "unicode": "1F1FB-1F1E6",
+ "digest": "fd3c1c5d0ac030e838f807288912c98a3e258f87901e252e46942a4dab9f8cb7"
+ },
+ {
+ "name": "flag_vc",
+ "unicode": "1F1FB-1F1E8",
+ "digest": "7cd554ea8ca817b5366701160274587ab44167ae5a89c430bbaf237ea18b7421"
+ },
+ {
+ "name": "flag_ve",
+ "unicode": "1F1FB-1F1EA",
+ "digest": "72930094fb088c1facabea07616035ec4771374358a90c3045219d087b350dd8"
+ },
+ {
+ "name": "flag_vg",
+ "unicode": "1F1FB-1F1EC",
+ "digest": "78a59afd368b7a8312bfdb2f49927ff09e6b8f46aab0136c0453e3319e81df49"
+ },
+ {
+ "name": "flag_vi",
+ "unicode": "1F1FB-1F1EE",
+ "digest": "e070879f9605a9bae66bb84f2abf5a40c8b264baee65cd4f7a6720b826739f29"
+ },
+ {
+ "name": "flag_vn",
+ "unicode": "1F1FB-1F1F3",
+ "digest": "100ddf06e0f239b170f4d6cb459450bf4945281ee818f7d3c061828b80562219"
+ },
+ {
+ "name": "flag_vu",
+ "unicode": "1F1FB-1F1FA",
+ "digest": "59fc9d16818295bba4f7f551598f85378cd07f2bd7e31a4eef2589aaa3847563"
+ },
+ {
+ "name": "flag_wf",
+ "unicode": "1F1FC-1F1EB",
+ "digest": "62627702e3e3768808c12f153a527ffcc492ad74d8cdc1858cfde971efd0c8ee"
+ },
+ {
+ "name": "flag_white",
+ "unicode": "1F3F3",
+ "digest": "96307e3a28e92d1e7147a06f154ffc291ee3cd1765cf8b7bfb06412294112559"
+ },
+ {
+ "name": "flag_ws",
+ "unicode": "1F1FC-1F1F8",
+ "digest": "0c95271d0f4b23f0d215ee0fba05cf08ecb70665d4c028e17463ecda2754b164"
+ },
+ {
+ "name": "flag_xk",
+ "unicode": "1F1FD-1F1F0",
+ "digest": "713aa7d228e96f4a06d58d1fb8c2a55296c3e56842f8177ca936f3e09f50da1e"
+ },
+ {
+ "name": "flag_ye",
+ "unicode": "1F1FE-1F1EA",
+ "digest": "3bb65bae9c913357bcae8b8b5878efc9e194ca308442ab69639c29716b49f078"
+ },
+ {
+ "name": "flag_yt",
+ "unicode": "1F1FE-1F1F9",
+ "digest": "f86c86f4c194610a3af78971fcf221ad97b9499d08f6d64476e417a2f52a611e"
+ },
+ {
+ "name": "flag_za",
+ "unicode": "1F1FF-1F1E6",
+ "digest": "4dd4fa49a01fdcfc7c1c099a7869e0e9acba83a6a3debf6c8505ada4c796b872"
+ },
+ {
+ "name": "flag_zm",
+ "unicode": "1F1FF-1F1F2",
+ "digest": "ab6790d89875447de3d1c7f4713b102761bc3e9afdd714b818689e175ca03011"
+ },
+ {
+ "name": "flag_zw",
+ "unicode": "1F1FF-1F1FC",
+ "digest": "9d39b934fe922174b2250f2cd1b174a548d2904091d3298f35b7cc59fbceb181"
+ },
+ {
+ "name": "flags",
+ "unicode": "1F38F",
+ "digest": "c3f4a66786e524a5562919afcba9486113091ed205f1342e91d2f6439845ad61"
+ },
+ {
+ "name": "flashlight",
+ "unicode": "1F526",
+ "digest": "5f641b8fd1c7f1dcd43ec3b1ef78d14ef9929d723789c5567aca8b95d3d39803"
+ },
+ {
+ "name": "fleur-de-lis",
+ "unicode": "269C",
+ "digest": "d6ddeeea355ed55103b7fc65ac1ee0dbaa79d01e0d136b265363a6b92284c073"
+ },
+ {
+ "name": "flip_phone",
+ "unicode": "1F581",
+ "digest": "be59efba4bc0759af5a726c06619090ef5071bf2541611d71691dedecee6c697"
+ },
+ {
+ "name": "floppy_black",
+ "unicode": "1F5AA",
+ "digest": "9022f51bb09c5130c6d46bb2accb159bed6f54d6fbffda6ecad62965ebc958ea"
+ },
+ {
+ "name": "floppy_disk",
+ "unicode": "1F4BE",
+ "digest": "e987961ca516032a90942ef6c398836f2da68a5981714bd172acfe7b0e369d0a"
+ },
+ {
+ "name": "floppy_white",
+ "unicode": "1F5AB",
+ "digest": "ec79c400117c4506ef8cf3eebef6c42dd37e60b3079d3e98b6ccd06e517e2af0"
+ },
+ {
+ "name": "flower_playing_cards",
+ "unicode": "1F3B4",
+ "digest": "451f361050b96ba9ed8dc5b64c8a90c1316fd9b83fb818152881a54e100eea6c"
+ },
+ {
+ "name": "flushed",
+ "unicode": "1F633",
+ "digest": "39cf51f9dec2a910c66ecd39a7bd616fea09d67e81801e57e84f03ed1e917750"
+ },
+ {
+ "name": "fog",
+ "unicode": "1F32B",
+ "digest": "da6fdb9b682ed9a3368adcd7531f1a29e22755a620e3cca163fc3f33a6a78107"
+ },
+ {
+ "name": "foggy",
+ "unicode": "1F301",
+ "digest": "b599f3178db289c6e30017f3f0a9d30b00a75417057c7a10c0c9eedac78edbf1"
+ },
+ {
+ "name": "folder",
+ "unicode": "1F5C0",
+ "digest": "8932141321911032ce8469ba85fe309b78384545c3b9946978b383670b956644"
+ },
+ {
+ "name": "folder_open",
+ "unicode": "1F5C1",
+ "digest": "74f3b484771c3d6ef61cf003de25c1a59b875afa46c057b5b1d92d9f99460685"
+ },
+ {
+ "name": "football",
+ "unicode": "1F3C8",
+ "digest": "834fe5f431d6aa8ef1186aa79e71f813393535d273483b6af4cc4bdb8380e5b4"
+ },
+ {
+ "name": "footprints",
+ "unicode": "1F463",
+ "digest": "60dc938f6769ea21b05b5afcc481d3ddacf1f565e04f33310b271d5422e7ceb9"
+ },
+ {
+ "name": "fork_and_knife",
+ "unicode": "1F374",
+ "digest": "7e07c9dc555d172fa2eaa41cefd8d46d9624be0137aff196dd003a8a82610ec3"
+ },
+ {
+ "name": "fork_knife_plate",
+ "unicode": "1F37D",
+ "digest": "b4081b9edea6cdab5112fdd17535051ba17710953013f5020c7c40f84a1e3247"
+ },
+ {
+ "name": "fountain",
+ "unicode": "26F2",
+ "digest": "0acdca5e8f6d745a8d582d96012ec8fc55b9f5447e657ebfd998a4e332d99322"
+ },
+ {
+ "name": "four",
+ "unicode": "0034-20E3",
+ "digest": "36bd4ea6e2ae689835a79f8e60466eccd62fce7e91e84ed768cffd87dac628dd"
+ },
+ {
+ "name": "four_leaf_clover",
+ "unicode": "1F340",
+ "digest": "12ee2343df25bbd9077fdc12314c1edb51c0cdb556af7e22590e8a578ef57f17"
+ },
+ {
+ "name": "frame_photo",
+ "unicode": "1F5BC",
+ "digest": "6ff21063063989c6ae7dd69f4d6a781c676f9dba380d8e6f1dbac5d53b24f349"
+ },
+ {
+ "name": "frame_tiles",
+ "unicode": "1F5BD",
+ "digest": "34a5bb044b4b3ad94b116ad106f7b6747fb8612dc0e9f8ccd4313c2920508df0"
+ },
+ {
+ "name": "frame_x",
+ "unicode": "1F5BE",
+ "digest": "2e427688fd70361c8c59787d0722ad68abe1c3f968258ee99c0c77ce4b8a8e15"
+ },
+ {
+ "name": "free",
+ "unicode": "1F193",
+ "digest": "c1d9172a656717f78d941303c5da8790c6cd9827838d8f7dc3719afb53bcab80"
+ },
+ {
+ "name": "fried_shrimp",
+ "unicode": "1F364",
+ "digest": "c0c19e95f2c38f6cf870920bf3c2d4d69c36ea6e7dc9a5c45c3e8b285269d40a"
+ },
+ {
+ "name": "fries",
+ "unicode": "1F35F",
+ "digest": "0f546534684de29d319cbcbab4162acb321c4f8f3202fe17d69e1894ab7c8195"
+ },
+ {
+ "name": "frog",
+ "unicode": "1F438",
+ "digest": "6a417757fa6ee39e7a277cbd53c690ff88af0b1d76728d56f9bc645cb628aeb7"
+ },
+ {
+ "name": "frowning",
+ "unicode": "1F626",
+ "digest": "fb39f5c2aea98054adb02a3a0ac34a2e38d83f32cd590e9d2449e06a9702f2f5"
+ },
+ {
+ "name": "frowning2",
+ "unicode": "2639",
+ "digest": "7bb6c682a6c9f98bf3a5ae986e317fd26d1af497c857500deec2f06b6a3af5da"
+ },
+ {
+ "name": "fuelpump",
+ "unicode": "26FD",
+ "digest": "9cbb2646c93b255bd3de87dc01aa1193ab96e39a3013975d250472ab8aae61d6"
+ },
+ {
+ "name": "full_moon",
+ "unicode": "1F315",
+ "digest": "0b4f08ef2089397ead034b444a60e6e9810073454581b52a46b2369e3b9cd5f9"
+ },
+ {
+ "name": "full_moon_with_face",
+ "unicode": "1F31D",
+ "digest": "a371cb9e1f28a7db739dd058234642a2e333dff4b6df9882df85a6d984e4b5e8"
+ },
+ {
+ "name": "game_die",
+ "unicode": "1F3B2",
+ "digest": "6584909a4348c350c04417421b63eace1245087f7d239051b30a0cd37fe929f9"
+ },
+ {
+ "name": "gear",
+ "unicode": "2699",
+ "digest": "b0ff5fd007daa366a9eecb7422dbeb8a973e123a04267b88fef96c7453238294"
+ },
+ {
+ "name": "gem",
+ "unicode": "1F48E",
+ "digest": "d75d854f35975e4e291c3b9fcaf8437467f6d7eb27b29e2d7c0f0038fc666fe2"
+ },
+ {
+ "name": "gemini",
+ "unicode": "264A",
+ "digest": "392abe62872736a0bf92979a8c25a814985d0ff0a08dc7ab2a5c058aeda7e685"
+ },
+ {
+ "name": "ghost",
+ "unicode": "1F47B",
+ "digest": "f084b14483476e2d07563840f8c33b46da9c17f791da07fde3acffeb77342947"
+ },
+ {
+ "name": "gift",
+ "unicode": "1F381",
+ "digest": "c9a2ae6ea05c02e78e9567dcbd971701a2f869eb46c62d85cef23d0834388d8c"
+ },
+ {
+ "name": "gift_heart",
+ "unicode": "1F49D",
+ "digest": "e0c5aacf1ce89117d86b148f10a02dc18fe0cd22a75fbf6f0f88f2fad3ca80fe"
+ },
+ {
+ "name": "girl",
+ "unicode": "1F467",
+ "digest": "0758cbc4cbc7d72d6df8f66fc3a6b2b283c6634b053e59d61c6cac44cf8bffda"
+ },
+ {
+ "name": "girl_tone1",
+ "unicode": "1F467-1F3FB",
+ "digest": "7afdece55cb64e8056e2202de8c17b66ddb616f224ac374ec9a160d06b3138cc"
+ },
+ {
+ "name": "girl_tone2",
+ "unicode": "1F467-1F3FC",
+ "digest": "c160aa65fee70ad52930d01246ac9f282ff6abf1d93c5cc5b299fc257ee81db1"
+ },
+ {
+ "name": "girl_tone3",
+ "unicode": "1F467-1F3FD",
+ "digest": "b8a5687cd637855a41b8c7dc686f0e69fda379875408cd269f1b330a805c72f4"
+ },
+ {
+ "name": "girl_tone4",
+ "unicode": "1F467-1F3FE",
+ "digest": "a9cf743936b733634f323790a1abe3a410601b6841484baebea484b392f4e98e"
+ },
+ {
+ "name": "girl_tone5",
+ "unicode": "1F467-1F3FF",
+ "digest": "c902170e67b81eee35eeefb6a5c62c6109cb423dcae88d4e036ddd50b240c072"
+ },
+ {
+ "name": "girls_symbol",
+ "unicode": "1F6CA",
+ "digest": "2c55aee81defd7a1620ffeaad8d9bcc1835f19237c72c79633aec45671ddb9ff"
+ },
+ {
+ "name": "globe_with_meridians",
+ "unicode": "1F310",
+ "digest": "945646de3d8f057760fe374494a253d9a6aa8a132309154b0a5bdbffb5b20c3f"
+ },
+ {
+ "name": "goat",
+ "unicode": "1F410",
+ "digest": "f99cbc6755d119cb5c1dce08cabd20871f98d009bb773da4a146dae60476a235"
+ },
+ {
+ "name": "golf",
+ "unicode": "26F3",
+ "digest": "74a7876d185f8ff6a6533e4db2e1eb787119b2f8d8b07c36d99ec3163fb48485"
+ },
+ {
+ "name": "golfer",
+ "unicode": "1F3CC",
+ "digest": "6458295a5e4a6e4323c32a7f1f7182fb2d3918083839efc380d995860ce360b1"
+ },
+ {
+ "name": "grapes",
+ "unicode": "1F347",
+ "digest": "7f6873d65180ab476f49d207ac2d1f7dbaf6c8b0b561d50b64325e192cf97a86"
+ },
+ {
+ "name": "green_apple",
+ "unicode": "1F34F",
+ "digest": "effc3fe60f2ab704a034c794bfccfa023b41332f8f16ca44cc8ea41698f03873"
+ },
+ {
+ "name": "green_book",
+ "unicode": "1F4D7",
+ "digest": "6652c4d2ccfa4a287a5d45007bd06cadc16d34b0a1ca4b6b13b46f976c8d8319"
+ },
+ {
+ "name": "green_heart",
+ "unicode": "1F49A",
+ "digest": "f4bcb660a1d3cf3692238359d8b9de9a725a9af81f166253e487d61b8ccf9d86"
+ },
+ {
+ "name": "grey_exclamation",
+ "unicode": "2755",
+ "digest": "ac8cdab7496d133e7bc9475f2fdb0cf59b3ccba20f2f156c8b693e72b5948078"
+ },
+ {
+ "name": "grey_question",
+ "unicode": "2754",
+ "digest": "c173e1b2a16ab62b0abd7a58deb7a6df709b072d30d001627b92d0123a3a3e4a"
+ },
+ {
+ "name": "grimacing",
+ "unicode": "1F62C",
+ "digest": "8c54b73f5d2c1c6347e2c0ab01616519e0fb34490daa9c36664d442c6851c57e"
+ },
+ {
+ "name": "grin",
+ "unicode": "1F601",
+ "digest": "916eabdabd8b7ca698e638bbbd14affff97464ec11a3b59c0cb96cd7705600d8"
+ },
+ {
+ "name": "grinning",
+ "unicode": "1F600",
+ "digest": "3d8665c03f272ca3063e96145989926355a7ac315ed1a032d30fcefa6f0c3923"
+ },
+ {
+ "name": "guardsman",
+ "unicode": "1F482",
+ "digest": "ebbd29fa138005232d64fca4a8ec015d097fa14e6ded57b35ac257b4570b3c36"
+ },
+ {
+ "name": "guardsman_tone1",
+ "unicode": "1F482-1F3FB",
+ "digest": "b6082c8fee5dbc3ce2540f3939d5e344b5366c9f07827345facaba438e7017ff"
+ },
+ {
+ "name": "guardsman_tone2",
+ "unicode": "1F482-1F3FC",
+ "digest": "2b813afe1c2bbdaf9a47493393a0e6c400a16e453ed25a9a9c0035197927b56e"
+ },
+ {
+ "name": "guardsman_tone3",
+ "unicode": "1F482-1F3FD",
+ "digest": "49b2fa1ad0bc50a5ef6d73fb140aa1876506b9ebb9d45782ccb8dbb6818f8dde"
+ },
+ {
+ "name": "guardsman_tone4",
+ "unicode": "1F482-1F3FE",
+ "digest": "a584e1e3a8ad7be4871a6bdb7996d4f649abeaa77eb5d1cae998058d8b23ca0f"
+ },
+ {
+ "name": "guardsman_tone5",
+ "unicode": "1F482-1F3FF",
+ "digest": "e853b67ee13fda99e98f47083529ca80c404df1b19352c78b9c69850eb8f2c76"
+ },
+ {
+ "name": "guitar",
+ "unicode": "1F3B8",
+ "digest": "8c041b961649cc5917f56f2fb543f9a5280724647ed2fc67bc94a05eff9da805"
+ },
+ {
+ "name": "gun",
+ "unicode": "1F52B",
+ "digest": "d7f5aa657cc0ba04d878511820632b89c305a9b4d6c4a4b90ff691dad9906607"
+ },
+ {
+ "name": "haircut",
+ "unicode": "1F487",
+ "digest": "369dbab1b138c31d3eca04c950fdab4ec9f085272268c241f100d44e7b0f229e"
+ },
+ {
+ "name": "haircut_tone1",
+ "unicode": "1F487-1F3FB",
+ "digest": "c56f32d7c1d8a92d22429133f87f31a159818939cfdc570cb48b6d243cc58cf2"
+ },
+ {
+ "name": "haircut_tone2",
+ "unicode": "1F487-1F3FC",
+ "digest": "e916e040ffb8e869e930d1256343af2ad2bbaa683f01a11564d0777019944bec"
+ },
+ {
+ "name": "haircut_tone3",
+ "unicode": "1F487-1F3FD",
+ "digest": "f07cdfbea964ac42a9a050f832107ef0f2fa8115b27689f93d1be954de07b7c1"
+ },
+ {
+ "name": "haircut_tone4",
+ "unicode": "1F487-1F3FE",
+ "digest": "32ec7f5e999f7c43676768c8320ffaa346c713d340a94b948b1f564b345a2d11"
+ },
+ {
+ "name": "haircut_tone5",
+ "unicode": "1F487-1F3FF",
+ "digest": "5aad997d09e7975700927906d41a10bae774356ccddbe5197980bde670272262"
+ },
+ {
+ "name": "hamburger",
+ "unicode": "1F354",
+ "digest": "24ebae9a69cf283ab198499cb38d0cdcd82bac74c8e8d1e769ad78eb320a4294"
+ },
+ {
+ "name": "hammer",
+ "unicode": "1F528",
+ "digest": "a43a66b0efdc4cd2c84fd0ccc2cb8e9ede1f89c5d62eefa6ae521d3aed9d81b3"
+ },
+ {
+ "name": "hammer_pick",
+ "unicode": "2692",
+ "digest": "2e4fe33406ca03fbb0df1596d63e903d8ee6bd78ecc3ec38a67dd2cecbc584e2"
+ },
+ {
+ "name": "hamster",
+ "unicode": "1F439",
+ "digest": "f47da088ff5792532a382b6e3a47d2dd7c5e6fc19abd5ff6c5ba3ce420b4192e"
+ },
+ {
+ "name": "hand_splayed",
+ "unicode": "1F590",
+ "digest": "a43e52f7cdec5e9d51497888b0988d7bbd42846ad7e492b196293fbce576d197"
+ },
+ {
+ "name": "hand_splayed_reverse",
+ "unicode": "1F591",
+ "digest": "ff0af0fe9def7388adca6836e5958492282b1afae99f1b6e1e65d11ba68b96db"
+ },
+ {
+ "name": "hand_splayed_tone1",
+ "unicode": "1F590-1F3FB",
+ "digest": "73cceec7117280d330f8a149979190f0f355dd8d0a92821be89fb70344bb8dfe"
+ },
+ {
+ "name": "hand_splayed_tone2",
+ "unicode": "1F590-1F3FC",
+ "digest": "b06fac698128f4c3a7b8ea56e8bc4de088bb5461aa0f9c84553f16b43d347145"
+ },
+ {
+ "name": "hand_splayed_tone3",
+ "unicode": "1F590-1F3FD",
+ "digest": "a94ee9a2f8cdec6d2f7dd6887d1c7b8e064fcad63030c2c7c001742d72b5603e"
+ },
+ {
+ "name": "hand_splayed_tone4",
+ "unicode": "1F590-1F3FE",
+ "digest": "501792b4126c6f32e755accee0fc8b4d1915e1d36c4ceaa40f3bd0066efe76c3"
+ },
+ {
+ "name": "hand_splayed_tone5",
+ "unicode": "1F590-1F3FF",
+ "digest": "22ed533d587cf44f286e2d6ad77be20b4b5f133c422af4ca51e9af86a75002d8"
+ },
+ {
+ "name": "hand_victory",
+ "unicode": "1F594",
+ "digest": "2d512ced4e8a438f2a346aed67310d3080f9828c748ade1be95943c32ba1c735"
+ },
+ {
+ "name": "handbag",
+ "unicode": "1F45C",
+ "digest": "f1e2822c67f659b52c76821dd9db001332215a8566fc1846c89b6019c9758038"
+ },
+ {
+ "name": "hard_disk",
+ "unicode": "1F5B4",
+ "digest": "df8549d4281f5ae70fb6792a02c078e651764b0276aa43b7407236bd38fc21b4"
+ },
+ {
+ "name": "hash",
+ "unicode": "0023-20E3",
+ "digest": "5bd5c7180485fa71accdec5378bdc196ce0602f594f91e4eadc1e7514d5d0f90"
+ },
+ {
+ "name": "hatched_chick",
+ "unicode": "1F425",
+ "digest": "7995c3eb503a8b9662694eba80a9b551216473a31928091e35cd6ebc21cee083"
+ },
+ {
+ "name": "hatching_chick",
+ "unicode": "1F423",
+ "digest": "22905b42fa65dbc9aad8940d2db13691cacc62014f54e0960978ee0002178e1b"
+ },
+ {
+ "name": "head_bandage",
+ "unicode": "1F915",
+ "digest": "d690b740ff4f58e89dfc764c6411a4e84cfedffd7694eb5efa839a642dbabd08"
+ },
+ {
+ "name": "headphones",
+ "unicode": "1F3A7",
+ "digest": "219da138032c01c97a94f02b211049418191a3beb3d159804b9033f5916fd3c8"
+ },
+ {
+ "name": "hear_no_evil",
+ "unicode": "1F649",
+ "digest": "8120060238eaca645809dd113862a144f10395afcb3837ab60c0f04009b49a2f"
+ },
+ {
+ "name": "heart",
+ "unicode": "2764",
+ "digest": "a646a25a36f431cadc7e56afd1a4d1b7cbae5292a25d7783bd31462d0d3d719b"
+ },
+ {
+ "name": "heart_decoration",
+ "unicode": "1F49F",
+ "digest": "a83989669347c98cb74065d4f0befedbc37f82c91214e773245cb6810ab359b4"
+ },
+ {
+ "name": "heart_exclamation",
+ "unicode": "2763",
+ "digest": "9751c89dcf10805f2011949ff3ddcb6bcb13de8c32ae5de9e03955e8a4235df2"
+ },
+ {
+ "name": "heart_eyes",
+ "unicode": "1F60D",
+ "digest": "335ea73efca4824e623a5a51ccdb494c8b1f5f10b4139b39b250a2a771876b0d"
+ },
+ {
+ "name": "heart_eyes_cat",
+ "unicode": "1F63B",
+ "digest": "9346b85afb80f7b498cc255426ea15a287f81d8fb3c26dab61337635f439d3ce"
+ },
+ {
+ "name": "heart_tip",
+ "unicode": "1F394",
+ "digest": "2178829e2c85accda55d2f685544587f6de5c8398a127ae1e08ff1c4ab282204"
+ },
+ {
+ "name": "heartbeat",
+ "unicode": "1F493",
+ "digest": "cd6921ce55c155873220a09416d695c4bcca1556007066d6d185e93d6561e825"
+ },
+ {
+ "name": "heartpulse",
+ "unicode": "1F497",
+ "digest": "f869357b9e678d9671ec38c569fc88efec48006c159b69297277cee795dc4dc9"
+ },
+ {
+ "name": "hearts",
+ "unicode": "2665",
+ "digest": "17dc9b2941561f58ca0f04d0754b1eff3490b63b17241580b3d4aa4638fa85e8"
+ },
+ {
+ "name": "heavy_check_mark",
+ "unicode": "2714",
+ "digest": "b5fa24f6e0f1dcbd6278e9125154522f2efd79e6dd0836ccb792a1f3aeeff2b2"
+ },
+ {
+ "name": "heavy_division_sign",
+ "unicode": "2797",
+ "digest": "59a6983d788f347c64eecb3df6f7d3b36779d92df6cc811820993ff9e18d77e1"
+ },
+ {
+ "name": "heavy_dollar_sign",
+ "unicode": "1F4B2",
+ "digest": "d2e89c54b3fdeda4d1fd4d29454b69dcf750181110894e6e71a40df99c95bfe8"
+ },
+ {
+ "name": "heavy_minus_sign",
+ "unicode": "2796",
+ "digest": "dd5ab3722fe49cfdbc5e1fbab5b342dc960de7b412d4fba59d66e06ce3dc3bcd"
+ },
+ {
+ "name": "heavy_multiplication_x",
+ "unicode": "2716",
+ "digest": "7d77742f91377785675802f40bd8dde9bd1feeb513735760a58ea9bee8a65d44"
+ },
+ {
+ "name": "heavy_plus_sign",
+ "unicode": "2795",
+ "digest": "9aa9dcdbba120a4b485c21f67589609b789c6e3edf08479ff8268fa0db973ad7"
+ },
+ {
+ "name": "helicopter",
+ "unicode": "1F681",
+ "digest": "b259ea8d2bdca36766075894da650b1d3ff4c8602259cd0d30cb8214cd585340"
+ },
+ {
+ "name": "helmet_with_cross",
+ "unicode": "26D1",
+ "digest": "affbe9dd87b87ff9235b4858c59c2a73e9ed30dd5221e5b666b8d7747378a9c4"
+ },
+ {
+ "name": "herb",
+ "unicode": "1F33F",
+ "digest": "3c452106b1966f643751bf161fa7d1762a33e6fff381b2109bb53b55c4fdd129"
+ },
+ {
+ "name": "hibiscus",
+ "unicode": "1F33A",
+ "digest": "268963a1f3cdad9050d9ae31c558e010f33812e3b09bbf9088ba876c033d8b2f"
+ },
+ {
+ "name": "high_brightness",
+ "unicode": "1F506",
+ "digest": "d607f6269d95dd16c2a7932e49ac09e44f4c19e0a34f6c0f21ecb945a2316361"
+ },
+ {
+ "name": "high_heel",
+ "unicode": "1F460",
+ "digest": "5c320d5954bf4f4dacacddd562c1598ab101731077a6656ac5d2bfd41405483e"
+ },
+ {
+ "name": "hockey",
+ "unicode": "1F3D2",
+ "digest": "008904c1b8db139215492a6d96c09f2c3eeda769f858a9bbae13f8c54d439d0e"
+ },
+ {
+ "name": "hole",
+ "unicode": "1F573",
+ "digest": "36bbafa5e89b1410ec74919aaf60b09ac3525a421cb5b475b9bb2f20357db8de"
+ },
+ {
+ "name": "homes",
+ "unicode": "1F3D8",
+ "digest": "9980d6dd6cbd23b820747ecac4cb10974dd24b0c94b4acfe21fa87793ad065c9"
+ },
+ {
+ "name": "honey_pot",
+ "unicode": "1F36F",
+ "digest": "94cb1624491076b5cb145e7a309f91a7be3d4c0bed712af6a51d641eb73edee7"
+ },
+ {
+ "name": "horse",
+ "unicode": "1F434",
+ "digest": "624ad9dc9ed7af3f6e1a2f9d4ed483702ae64ed5fbcf5e9918af6bfef24e76f9"
+ },
+ {
+ "name": "horse_racing",
+ "unicode": "1F3C7",
+ "digest": "c2702b7225e9839a789dda7c43f0cc86dced2b4d5d3787116106396633362de6"
+ },
+ {
+ "name": "horse_racing_tone1",
+ "unicode": "1F3C7-1F3FB",
+ "digest": "a7ed284f9d5cd8a4fe4a09cb91c3f99e5db99c7e31c5f525c14de97b06857d92"
+ },
+ {
+ "name": "horse_racing_tone2",
+ "unicode": "1F3C7-1F3FC",
+ "digest": "20b4d61b21ee6ba860b029f0ad0e38f5ecb6dd2c774f7b7801fba07ed33f96be"
+ },
+ {
+ "name": "horse_racing_tone3",
+ "unicode": "1F3C7-1F3FD",
+ "digest": "dd65f7bb96ee44507d26e524202d567d2d7679d571245299a2a84f68bd5def4c"
+ },
+ {
+ "name": "horse_racing_tone4",
+ "unicode": "1F3C7-1F3FE",
+ "digest": "36afaad218a4c820b19c7c9bbbc187119d47b41273d8f48ab14cc3e32dd7c21f"
+ },
+ {
+ "name": "horse_racing_tone5",
+ "unicode": "1F3C7-1F3FF",
+ "digest": "2e0efd501a4471428533ce7909972a49ff045369261c27e4abb97ee2aede2f47"
+ },
+ {
+ "name": "hospital",
+ "unicode": "1F3E5",
+ "digest": "df5c774fa36b2601e6960a7b81cdfac71c1d2d71f04dea88068d1c9043e313bb"
+ },
+ {
+ "name": "hot_pepper",
+ "unicode": "1F336",
+ "digest": "62e4dade3c793f6d83530bd1f60f3e3e26c1e10a41786c3a15f5aec0ff2b8e76"
+ },
+ {
+ "name": "hotdog",
+ "unicode": "1F32D",
+ "digest": "58b829e26b5c4642942898d9c7873cb08e048fd7deaacba8292899d5d895cb2b"
+ },
+ {
+ "name": "hotel",
+ "unicode": "1F3E8",
+ "digest": "428120a35b38a217901e10d704751eb8fdbc9f805e6eccd8aab070f4311b2085"
+ },
+ {
+ "name": "hotsprings",
+ "unicode": "2668",
+ "digest": "df4f946218445f97a6f28c6abe4c1d1dac56ff97a8cd81df59f1b3c320e0092f"
+ },
+ {
+ "name": "hourglass",
+ "unicode": "231B",
+ "digest": "07aece9413e6898717b4f0757e073d7a593f3e8044c56855127033b796207ccb"
+ },
+ {
+ "name": "hourglass_flowing_sand",
+ "unicode": "23F3",
+ "digest": "92dbc68e9d16fb9f706236367e1882f0d2b6817b83ca490820a000021f2c6483"
+ },
+ {
+ "name": "house",
+ "unicode": "1F3E0",
+ "digest": "a6221fc84a9b0e11ae71bfa1e0020982b55ff8c89a374a6d755dba710b4e058c"
+ },
+ {
+ "name": "house_abandoned",
+ "unicode": "1F3DA",
+ "digest": "e404631e3a296bdeae3de7510da8934c32327bc0fa0f7ae4e676b61932165668"
+ },
+ {
+ "name": "house_with_garden",
+ "unicode": "1F3E1",
+ "digest": "22d0d911da96b7ae3bf6692d3cf3590afbca959fc99c13e7a088f7194f43a35d"
+ },
+ {
+ "name": "hugging",
+ "unicode": "1F917",
+ "digest": "68ed6c4e0eae9071cf67770a39e07a2290b4f7763170f765b3cd3ac67ae43240"
+ },
+ {
+ "name": "hushed",
+ "unicode": "1F62F",
+ "digest": "69faa8e0b170ee8cf41977ca4a5154406360ed9699d5c62ecdaa01f50e8e4276"
+ },
+ {
+ "name": "ice_cream",
+ "unicode": "1F368",
+ "digest": "d48ec98a8789148b96c30f19595201a0f85ed899659d97d1d3596091162909ff"
+ },
+ {
+ "name": "ice_skate",
+ "unicode": "26F8",
+ "digest": "6fb044d9fbe62605f6728062c35c345ddd3ae4cc51203c925b0e69f1b3ef2dbf"
+ },
+ {
+ "name": "icecream",
+ "unicode": "1F366",
+ "digest": "abd5774157575dd304dc1a393244757853972c863861a654ca29b2d528e48b28"
+ },
+ {
+ "name": "id",
+ "unicode": "1F194",
+ "digest": "860ffb36d37d84e2c1cf0ab991b95c1cf73e458bef0e4d85bb0c1e26115cb2d1"
+ },
+ {
+ "name": "ideograph_advantage",
+ "unicode": "1F250",
+ "digest": "37892a5642cd49ef7828646f36f48b5a83dc02437624c05da428579256118030"
+ },
+ {
+ "name": "imp",
+ "unicode": "1F47F",
+ "digest": "f8c93d03bd9f1d5ef86738541e11695d6811bf6fef06759eba98321b6d038814"
+ },
+ {
+ "name": "inbox_tray",
+ "unicode": "1F4E5",
+ "digest": "066a2d75633eb50329496f6866b5b0645c2e48135a03118f1bf53244f8529043"
+ },
+ {
+ "name": "incoming_envelope",
+ "unicode": "1F4E8",
+ "digest": "ef6e5c5aa679d174181dae77113717f26e295778dde1e2c3bdf1d64de8a4af8c"
+ },
+ {
+ "name": "info",
+ "unicode": "1F6C8",
+ "digest": "59c35e77d5ee663c5d56f7d8af845ce8aeb9935e526ae4a06e02ae70e71212ca"
+ },
+ {
+ "name": "information_desk_person",
+ "unicode": "1F481",
+ "digest": "acae6d272e348aee87dd60360f16ac58cea7cb4e1ea962cc1655005c7f4aed27"
+ },
+ {
+ "name": "information_desk_person_tone1",
+ "unicode": "1F481-1F3FB",
+ "digest": "709ebb0481ca981d76ece2d4fc68db693ddf18b9c1aaa0b6ac5d3c42e71bf07f"
+ },
+ {
+ "name": "information_desk_person_tone2",
+ "unicode": "1F481-1F3FC",
+ "digest": "d5bc3563bc721d66b73850db93ac827be3715e7ca6420dc0051396ffe26bef47"
+ },
+ {
+ "name": "information_desk_person_tone3",
+ "unicode": "1F481-1F3FD",
+ "digest": "af67fd4ef2fc402bec2d446b2e8ff5e9f636b5a9bbb6639587cdb88bd780d265"
+ },
+ {
+ "name": "information_desk_person_tone4",
+ "unicode": "1F481-1F3FE",
+ "digest": "fd3174d1adfe13e8c0d6b6ae9c3a26ea35bb40f98f0728f91d1798809a74933b"
+ },
+ {
+ "name": "information_desk_person_tone5",
+ "unicode": "1F481-1F3FF",
+ "digest": "4b773c443830a02de8b4d6471077b5d1387b560b537cabba7cdc667110cbde69"
+ },
+ {
+ "name": "information_source",
+ "unicode": "2139",
+ "digest": "50cd8bf46d20b7c18d5f00a69fc79452aa32934245ba8d0929e51632d73876bd"
+ },
+ {
+ "name": "innocent",
+ "unicode": "1F607",
+ "digest": "a3510fd51c17093ebe2371cfde7611aa44aed2d120a0e5500cfaae0f1d3486a4"
+ },
+ {
+ "name": "interrobang",
+ "unicode": "2049",
+ "digest": "1f843ff672486154f9f3df549bb1b528a5eac8d15264f447649ba57f45ee4d00"
+ },
+ {
+ "name": "iphone",
+ "unicode": "1F4F1",
+ "digest": "be6f96c02ddae557f700fd20fe7b3f94c9e1c928acb82b2b8b214d231273fece"
+ },
+ {
+ "name": "island",
+ "unicode": "1F3DD",
+ "digest": "17f02b309b62ed9542b1d8943168302846040e420f413e56d799bb5fba7064fa"
+ },
+ {
+ "name": "izakaya_lantern",
+ "unicode": "1F3EE",
+ "digest": "ddb20f475aa119c3a64a55dff40f7a9dbc3a14f7ffc6cfbac89210c652f10d02"
+ },
+ {
+ "name": "jack_o_lantern",
+ "unicode": "1F383",
+ "digest": "62a701ac472619bcb3859e0d9a61b98c7f5c32150d2d04ca8c3e8fc3bec4dbd5"
+ },
+ {
+ "name": "japan",
+ "unicode": "1F5FE",
+ "digest": "2535300fff2b2e4b75fc73c187be6c0ea4bc4753e443db498ea55e268e627ab7"
+ },
+ {
+ "name": "japanese_castle",
+ "unicode": "1F3EF",
+ "digest": "70645aa05599e23a9ac4327e4a2e78bffe7ea06c38ec1935c15ae420619c5c1c"
+ },
+ {
+ "name": "japanese_goblin",
+ "unicode": "1F47A",
+ "digest": "59b6901dc6eedc6509c25b4eef6702bf461ded06c5ff12fe2a02a5b3301577c0"
+ },
+ {
+ "name": "japanese_ogre",
+ "unicode": "1F479",
+ "digest": "dab7e68cd4cbf99c13d64792c7104c4f0a846bc63aa12950fa8fab028dca301d"
+ },
+ {
+ "name": "jeans",
+ "unicode": "1F456",
+ "digest": "ddd032ac77cdfe49152a0e0a0eaaaea9f183590fb1f493ec30e9e39f679e3914"
+ },
+ {
+ "name": "jet_up",
+ "unicode": "1F6E6",
+ "digest": "3708e5e034b1c64d1268d66527e13c369aa0f8903bce9172bef773b2d1940948"
+ },
+ {
+ "name": "joy",
+ "unicode": "1F602",
+ "digest": "f90cfbcb14f906f8d786b61f022c978f381fc99ca422805f605631314e101805"
+ },
+ {
+ "name": "joy_cat",
+ "unicode": "1F639",
+ "digest": "6ca24a94490de66d1ca2cbc080bcd805f54ca295051d8e6588cae3fe6658c80a"
+ },
+ {
+ "name": "joystick",
+ "unicode": "1F579",
+ "digest": "ec172df88ef8e8a5512d6d906c13296875b7057ed0cca79f4ac8cddd9e1de34b"
+ },
+ {
+ "name": "kaaba",
+ "unicode": "1F54B",
+ "digest": "30f1a27a148399bbb811586eff795eff858701c42055c23e4d5bef7ae77f5f32"
+ },
+ {
+ "name": "key",
+ "unicode": "1F511",
+ "digest": "c68ed648350d3976c8d27a709020c8873ecf553929e66453acff96231684a1a2"
+ },
+ {
+ "name": "key2",
+ "unicode": "1F5DD",
+ "digest": "87a7d42531d7a11dcb11b0d6d1be611ee8cec35b5d22226a8ac6083fedef4f5d"
+ },
+ {
+ "name": "keyboard",
+ "unicode": "1F5AE",
+ "digest": "3b254cbf19946df3af05e501d11653d89fcda91684b7248d86186f842b83bf16"
+ },
+ {
+ "name": "keyboard_mouse",
+ "unicode": "1F5A6",
+ "digest": "95b523e55d8afeaeb06442bbe20e47f49643bb0c32d89a8cdbbccdead20532b3"
+ },
+ {
+ "name": "keyboard_with_jacks",
+ "unicode": "1F398",
+ "digest": "e29a0d0b8018d13458469edca13c60a882a2817957c1aa11b050684c995a47ee"
+ },
+ {
+ "name": "keycap_ten",
+ "unicode": "1F51F",
+ "digest": "7593aa7ffe7192a2e35c6ccec76522f6243777783c9152c7c03419835ea58c03"
+ },
+ {
+ "name": "kimono",
+ "unicode": "1F458",
+ "digest": "e92bea044fe013f1993c2229d86e9cca9d43f14aab00564ce6ff559bdc5ce93a"
+ },
+ {
+ "name": "kiss",
+ "unicode": "1F48B",
+ "digest": "c060eb09af2a0d0f77d307b995c15719b0e59c9162a490b8a553fac9b779c8f0"
+ },
+ {
+ "name": "kiss_mm",
+ "unicode": "1F468-2764-1F48B-1F468",
+ "digest": "381364ad988ec07cc3708fd60f71838092224009088fff587069b4e8ab01ee63"
+ },
+ {
+ "name": "kiss_ww",
+ "unicode": "1F469-2764-1F48B-1F469",
+ "digest": "7705ca707b73f44c856ea324bdfe30ed05244c8d192d1111f6e1d62ab3f2f8a5"
+ },
+ {
+ "name": "kissing",
+ "unicode": "1F617",
+ "digest": "3142617e8b9488689bd9efc67c0e4cc71a1870df8ffc308f949eedc5c3684051"
+ },
+ {
+ "name": "kissing_cat",
+ "unicode": "1F63D",
+ "digest": "ed26cee8c438ba41365b55c48457cdad3e8d43bf90db3128ac5b277718b82ed3"
+ },
+ {
+ "name": "kissing_closed_eyes",
+ "unicode": "1F61A",
+ "digest": "22d3369d21b4c2cb4c0c2cab9551cd848dd4f9adecfa64977d3f1a80fc0c8b53"
+ },
+ {
+ "name": "kissing_heart",
+ "unicode": "1F618",
+ "digest": "1f089b07447bdcc1baada6a2a9607d4ef4f2de9a6093fcab47a553a64b9acb76"
+ },
+ {
+ "name": "kissing_smiling_eyes",
+ "unicode": "1F619",
+ "digest": "e37d282861669adfa3953b9af833acfab7d55e787621d4318d77de7e3529d5c5"
+ },
+ {
+ "name": "knife",
+ "unicode": "1F52A",
+ "digest": "3fef068a6ada61630dc868e47d25e0e0550b44bc7cf530afe88ca63dc7ab2a39"
+ },
+ {
+ "name": "koala",
+ "unicode": "1F428",
+ "digest": "fe020ab9048f3c2a881474f8b1335db6bfaf37d115ff9b2d264f668d136122dd"
+ },
+ {
+ "name": "koko",
+ "unicode": "1F201",
+ "digest": "734a5cb296826a598e02be3f4ec22f318633ede2ce274914586256421e2df97b"
+ },
+ {
+ "name": "label",
+ "unicode": "1F3F7",
+ "digest": "9fe8195c3efab4d905b1cfcba0ae58cda12496030b0908de8076ff5e6777742e"
+ },
+ {
+ "name": "large_blue_circle",
+ "unicode": "1F535",
+ "digest": "ba4d0f84a9c2be9a65b25c8cfa78f30d4856d021b1853154dd1d2fd0c5bcfb6a"
+ },
+ {
+ "name": "large_blue_diamond",
+ "unicode": "1F537",
+ "digest": "d5aa5e315126859c10c83507be6b9e11cbf423f7a27145de089468cff9b94a94"
+ },
+ {
+ "name": "large_orange_diamond",
+ "unicode": "1F536",
+ "digest": "108600badd0ef267842325c0fbf326cb3504306332c64f6f5694de2b54c9438a"
+ },
+ {
+ "name": "last_quarter_moon",
+ "unicode": "1F317",
+ "digest": "68315b85bc1cb17bb82629bd1a6024a5124f3641b9878a732a8aad016c587546"
+ },
+ {
+ "name": "last_quarter_moon_with_face",
+ "unicode": "1F31C",
+ "digest": "146a419109b7f662bf87cf9de299e47d025a8758c8970b7dabf3483e1956b559"
+ },
+ {
+ "name": "laughing",
+ "unicode": "1F606",
+ "digest": "f22d3be77f1daf058d04c3cbc1fd7f76b4dc069d2d300b45e63e768b08d269c5"
+ },
+ {
+ "name": "leaves",
+ "unicode": "1F343",
+ "digest": "f65e2db125564eb04fc427a49fff175d6e2dae847bd12314d5e6a131610d5ccd"
+ },
+ {
+ "name": "ledger",
+ "unicode": "1F4D2",
+ "digest": "62df1772cec10c035ae0646e6cca4ba7d75b10636a520d091c5b42c2dc36b742"
+ },
+ {
+ "name": "left_luggage",
+ "unicode": "1F6C5",
+ "digest": "62292758715115e55ab6239805b7f99b7b35bdfa8d40da07fe391424f1f083d8"
+ },
+ {
+ "name": "left_receiver",
+ "unicode": "1F57B",
+ "digest": "8052e44951afee04c87296128744b5019ec783c9ed1a231f659af6c8ddaa50f3"
+ },
+ {
+ "name": "left_right_arrow",
+ "unicode": "2194",
+ "digest": "28a6945972451b1f4dadec5c55310b8868ffd9f3b0a07803287bc4e07a56e7d4"
+ },
+ {
+ "name": "leftwards_arrow_with_hook",
+ "unicode": "21A9",
+ "digest": "d672afc39fd50f78d7370be243173fe76ba50292f0c401305b562898939a8b7f"
+ },
+ {
+ "name": "lemon",
+ "unicode": "1F34B",
+ "digest": "e0e293a8b8c1b3c87534f5e05cf006671eb3c6d52b4d17d40f2e23bce215a8be"
+ },
+ {
+ "name": "leo",
+ "unicode": "264C",
+ "digest": "b0fd4e5f4637de530b62323521c6edcd80312d67ea4043eedd959acb6763474a"
+ },
+ {
+ "name": "leopard",
+ "unicode": "1F406",
+ "digest": "ede891be8484a17e6277431c64ec1bfd6b742544a41947ebc85005bc2d558bb1"
+ },
+ {
+ "name": "level_slider",
+ "unicode": "1F39A",
+ "digest": "49777cf160d9130d723e3bfef765c3de54033e6b059000fb0e22fb559b5ed190"
+ },
+ {
+ "name": "levitate",
+ "unicode": "1F574",
+ "digest": "3e4e9a5ac6a8dbd7909c58a9d915f16f1a0fc59cc019714ae5935f18e4704044"
+ },
+ {
+ "name": "libra",
+ "unicode": "264E",
+ "digest": "ec8e2e7a735abc9f2bddb115fc0e09f4bdc7a164679e2b57d127f58eee1155c2"
+ },
+ {
+ "name": "lifter",
+ "unicode": "1F3CB",
+ "digest": "f64db037fd21e5918e5de35d6a561ef4b44668e307ed351338de00fcf3e771e3"
+ },
+ {
+ "name": "lifter_tone1",
+ "unicode": "1F3CB-1F3FB",
+ "digest": "f9e0d161b12c4908ac3409b11c1a77ee38f33ba018f12416545876214bfb7c01"
+ },
+ {
+ "name": "lifter_tone2",
+ "unicode": "1F3CB-1F3FC",
+ "digest": "631eb6ed5bd147dc6f1f8b94149abe44d62a0f78e7809e37a4bfe127c40ed98f"
+ },
+ {
+ "name": "lifter_tone3",
+ "unicode": "1F3CB-1F3FD",
+ "digest": "406b5707a47d9066f016acf0b64fa695e3505acc2453758a0428de21efd7eb6d"
+ },
+ {
+ "name": "lifter_tone4",
+ "unicode": "1F3CB-1F3FE",
+ "digest": "d917164ed8c4bb1ffcc887ca256ec329e7fa1b9516eaf8c159f8b43fdb071ed6"
+ },
+ {
+ "name": "lifter_tone5",
+ "unicode": "1F3CB-1F3FF",
+ "digest": "f79ea93e8a40b3c895b693bf49eb4ce6e7b3f4413595e5881ea44839fd7fe8e5"
+ },
+ {
+ "name": "light_check_mark",
+ "unicode": "1F5F8",
+ "digest": "7842b0df8c2b6703bed0cce5d2790d394eec7120b2a245a76f375528f2729a7b"
+ },
+ {
+ "name": "light_rail",
+ "unicode": "1F688",
+ "digest": "7c2be55456f1332e849ff6699a26dda2e1641c280f45c9ec88dedf6d9b7b7fe2"
+ },
+ {
+ "name": "link",
+ "unicode": "1F517",
+ "digest": "cc4873f8a612dd721dddcd507a4430b4fb6c4abc15a8848456f0ffd97811b163"
+ },
+ {
+ "name": "lion_face",
+ "unicode": "1F981",
+ "digest": "935b1076815f51fafcd860a395d0a03c536acfcea61ffcf542a377da046fa7d9"
+ },
+ {
+ "name": "lips",
+ "unicode": "1F444",
+ "digest": "e3bc20f9e210fa1711271234fe61bf1c9ddf36dd6ffc5b832c6c3a769a1e59a8"
+ },
+ {
+ "name": "lips2",
+ "unicode": "1F5E2",
+ "digest": "c6ba915982ac47d8aaf14ad3605949df95588acfb4e147bf608f8c1714cdf19b"
+ },
+ {
+ "name": "lipstick",
+ "unicode": "1F484",
+ "digest": "335b912e163020df3d6d9f0a19a55d6547bd59b471c5a3e374c2968e49911ccc"
+ },
+ {
+ "name": "lock",
+ "unicode": "1F512",
+ "digest": "c20eacfb8ccd9bb85919a837c0d4650ee608edb48c85bff46945f613e95d7038"
+ },
+ {
+ "name": "lock_with_ink_pen",
+ "unicode": "1F50F",
+ "digest": "5cab25cea08e22d9c3f5de16de6d0ab658ca15cc93d7830f29b0f3e9348ec45f"
+ },
+ {
+ "name": "lollipop",
+ "unicode": "1F36D",
+ "digest": "33d2334a00bf0e15869ccc75fadc36f27f89abf0525bb71f859aad9e1dc4ad66"
+ },
+ {
+ "name": "loop",
+ "unicode": "27BF",
+ "digest": "fa1174ddc44e317d0796e07868c7ac8ac9c9274fbc8a6c3d0ec78d543c3c6bf0"
+ },
+ {
+ "name": "loud_sound",
+ "unicode": "1F50A",
+ "digest": "fb70229e13b690ffc1031d2e631123f8c908035a15218c297c1c4a3ff3624aa0"
+ },
+ {
+ "name": "loudspeaker",
+ "unicode": "1F4E2",
+ "digest": "e2d6cf9ec6412ee62f3128a1afd8c63ec74755c4833f01a4f99722407fe154d6"
+ },
+ {
+ "name": "love_hotel",
+ "unicode": "1F3E9",
+ "digest": "184670ebc4045043a7b18d576da3255d216551da522a11cde7df34524e9c7d50"
+ },
+ {
+ "name": "love_letter",
+ "unicode": "1F48C",
+ "digest": "9a4c52e2622fc7d364995ebc93ca530d972134621d117b72053a659dffc90ffc"
+ },
+ {
+ "name": "low_brightness",
+ "unicode": "1F505",
+ "digest": "c177b7fa9fdbef959cc47e7d16becd71117470b767a81ed6d15f80f464776c02"
+ },
+ {
+ "name": "m",
+ "unicode": "24C2",
+ "digest": "2eaf011e74d69613923dad424daaec4c13b592388dbcc5757b645bc058eedecb"
+ },
+ {
+ "name": "mag",
+ "unicode": "1F50D",
+ "digest": "029427bd73d2c79fffc5194ded01f6011952ec0124b7634c6230e0afa7ad7c95"
+ },
+ {
+ "name": "mag_right",
+ "unicode": "1F50E",
+ "digest": "f99de50bb59ec3bf1d4ccb8584ca09d4a7ceb5bf9f600ea8d3f84930efbf01b8"
+ },
+ {
+ "name": "mahjong",
+ "unicode": "1F004",
+ "digest": "da5d1fa980c38e092d414516161ca26046aa65ace3261999ea750f72e676ac6e"
+ },
+ {
+ "name": "mailbox",
+ "unicode": "1F4EB",
+ "digest": "14217df8f39a95fc0a0c527f97db1ca8564764034e921614decc5be705629352"
+ },
+ {
+ "name": "mailbox_closed",
+ "unicode": "1F4EA",
+ "digest": "e0c7beb205ec548a66d8afc7f103b64c6c79c08417ab550f19c36cc6d1a62bc4"
+ },
+ {
+ "name": "mailbox_with_mail",
+ "unicode": "1F4EC",
+ "digest": "6d381c0c4181be628d9409df1d85f8a9438c21ef5b92d82ef8ae1ff0079236de"
+ },
+ {
+ "name": "mailbox_with_no_mail",
+ "unicode": "1F4ED",
+ "digest": "74843d5ea9e03b48323f2252bdd000585f549b7fffe1fe181a25c38b99b5e23d"
+ },
+ {
+ "name": "man",
+ "unicode": "1F468",
+ "digest": "0275935258b4c832c3fcb06531d3e6972e2c3d46bab2973004750a9f00bd4cb6"
+ },
+ {
+ "name": "man_tone1",
+ "unicode": "1F468-1F3FB",
+ "digest": "1f6603d040f4a025f49d384170dd16b8da169663fc3282af1dc8710d9c1a7adf"
+ },
+ {
+ "name": "man_tone2",
+ "unicode": "1F468-1F3FC",
+ "digest": "d65bb03071b483946c69c61769d19b29a2af76fa7e43020e55f0bbc046492221"
+ },
+ {
+ "name": "man_tone3",
+ "unicode": "1F468-1F3FD",
+ "digest": "9af8ede7211b19a7dc0c60db083dd2bdc4897dda4d71e57feadf2e39d847f060"
+ },
+ {
+ "name": "man_tone4",
+ "unicode": "1F468-1F3FE",
+ "digest": "6555de60976aafeb024db78addb44eab2a412dd7277013f44d06757d03b6a252"
+ },
+ {
+ "name": "man_tone5",
+ "unicode": "1F468-1F3FF",
+ "digest": "b58b97a28a6adc1777acc05194cd917c730f90e37441124c384ded12e9a7d2a4"
+ },
+ {
+ "name": "man_with_gua_pi_mao",
+ "unicode": "1F472",
+ "digest": "88663173a6ccbebec5e24883c90d965447e022c6688773273110fe544d5b1607"
+ },
+ {
+ "name": "man_with_gua_pi_mao_tone1",
+ "unicode": "1F472-1F3FB",
+ "digest": "3c8bad3923a619f888e14544d357499a26a517e8fbe7a51027117b960c9eb842"
+ },
+ {
+ "name": "man_with_gua_pi_mao_tone2",
+ "unicode": "1F472-1F3FC",
+ "digest": "da125a3310fab19c9282497d53e2fc71ad07920ce60a0ef52dcdb31500023f09"
+ },
+ {
+ "name": "man_with_gua_pi_mao_tone3",
+ "unicode": "1F472-1F3FD",
+ "digest": "1d5842558847367966bf3ea473ff80fe744359bc5d969f4cc06cf2e452ed2fb6"
+ },
+ {
+ "name": "man_with_gua_pi_mao_tone4",
+ "unicode": "1F472-1F3FE",
+ "digest": "92be490f3ba602a43e2be8160d8bfd8a0691b2f81fe017b06df10f476a89ffab"
+ },
+ {
+ "name": "man_with_gua_pi_mao_tone5",
+ "unicode": "1F472-1F3FF",
+ "digest": "669f6b31bc7a8bf50b169d0600f14e00addaeb24144a1bace8b94950372839b0"
+ },
+ {
+ "name": "man_with_turban",
+ "unicode": "1F473",
+ "digest": "87d30d35ba40ee39c2df8ce19d975ce34a9c54688bafeac7377d7d481e55f1a4"
+ },
+ {
+ "name": "man_with_turban_tone1",
+ "unicode": "1F473-1F3FB",
+ "digest": "33b8b8154e0691e2ad66177dbf1e0101411fd8b3a16bf4e54c36d4a874f2a275"
+ },
+ {
+ "name": "man_with_turban_tone2",
+ "unicode": "1F473-1F3FC",
+ "digest": "1a6b83faa8d6e6a7d12a04898a6f22243287330a1faa081d2626b17dfb07174d"
+ },
+ {
+ "name": "man_with_turban_tone3",
+ "unicode": "1F473-1F3FD",
+ "digest": "5d43da5109e688ff8ca0675f33ebbaf930e206f1f01e3ee773f2844663fe572b"
+ },
+ {
+ "name": "man_with_turban_tone4",
+ "unicode": "1F473-1F3FE",
+ "digest": "bfaf7293c5ea75d0ecdc6fe5afe8f48e7b29b2e0df06ef974d3e1732f5db5dd4"
+ },
+ {
+ "name": "man_with_turban_tone5",
+ "unicode": "1F473-1F3FF",
+ "digest": "fba2404dd3d7eab5268519894cc0b386e1b17fdf14a04760c346014aa0e25acd"
+ },
+ {
+ "name": "mans_shoe",
+ "unicode": "1F45E",
+ "digest": "45dc13ac44c922b4c4b8ecb2e1a870a78e09d53da86843431ab0e9ec96ebcd97"
+ },
+ {
+ "name": "map",
+ "unicode": "1F5FA",
+ "digest": "f56116d09996d6d08fb5cdfb46622b545253f2649008170fc2011a9713fa875b"
+ },
+ {
+ "name": "maple_leaf",
+ "unicode": "1F341",
+ "digest": "40c5ee93396301911391cf6e70454b6fa8020fe5c85d3364136bcedb5d052cdb"
+ },
+ {
+ "name": "mask",
+ "unicode": "1F637",
+ "digest": "e0301cd27eb8c74c9772ff05b880215fc031ac1ae7f3177cd24ba0acb43b3834"
+ },
+ {
+ "name": "massage",
+ "unicode": "1F486",
+ "digest": "856d0fb1144ee91c58dfad74f9a2cababf6bae4b3ceba2a95c03ecd44ae3aa21"
+ },
+ {
+ "name": "massage_tone1",
+ "unicode": "1F486-1F3FB",
+ "digest": "fd53b06eb0967303c0914ebb79fd872900ec0f71b2852c7238517e192e5023e1"
+ },
+ {
+ "name": "massage_tone2",
+ "unicode": "1F486-1F3FC",
+ "digest": "7ef57359a339ae1ca4488f9a6195a352e74daf5b67d8e1ae1e91fe866921c40c"
+ },
+ {
+ "name": "massage_tone3",
+ "unicode": "1F486-1F3FD",
+ "digest": "e4fb643b6242bedb395e503ae337a88b2a255b5fda88b4aaa93396f948614a6e"
+ },
+ {
+ "name": "massage_tone4",
+ "unicode": "1F486-1F3FE",
+ "digest": "94f007c2daf9455fa8d2b10cc7ccff7db9bc9daf835ef5c3699be091938db833"
+ },
+ {
+ "name": "massage_tone5",
+ "unicode": "1F486-1F3FF",
+ "digest": "d18e800b728bf45b500f492062dc81312ca1ad7b1a0277a3d5bc150e4632ea1c"
+ },
+ {
+ "name": "meat_on_bone",
+ "unicode": "1F356",
+ "digest": "674a2a58e174b7681eef3b6c5b39c098ed9374cc610d037166c0092ee5269a97"
+ },
+ {
+ "name": "medal",
+ "unicode": "1F3C5",
+ "digest": "270d438b6e2155e944dc734ea3e4d02409e51f59db2db636398fbf96e5edb0e6"
+ },
+ {
+ "name": "mega",
+ "unicode": "1F4E3",
+ "digest": "540ab4fd5bab041a681749b85e6de598ebcbfc4fbf5c3cdbd9ca1e8256191733"
+ },
+ {
+ "name": "melon",
+ "unicode": "1F348",
+ "digest": "39dd0ecb23e2d3da6cbb7309333fed5d7e2cb38c0afc526ade78520eca11b5f4"
+ },
+ {
+ "name": "menorah",
+ "unicode": "1F54E",
+ "digest": "5f81bc2e5a34bf76481d2958fdb0b4e4540c599aa837a6453609a39023885d8c"
+ },
+ {
+ "name": "mens",
+ "unicode": "1F6B9",
+ "digest": "5ed56cff80e8ee7ed581f2a2e365915db5cb29df89e850e0add0b68db4b0c788"
+ },
+ {
+ "name": "metal",
+ "unicode": "1F918",
+ "digest": "45e5fac0b9b019cf217dcfd1380cafb0d03063454612178278dac1ca5f8476a6"
+ },
+ {
+ "name": "metal_tone1",
+ "unicode": "1F918-1F3FB",
+ "digest": "9b3596fe7c063df838f0a43fb680ce10fb88e2b73c5c3324abfa357a224c17aa"
+ },
+ {
+ "name": "metal_tone2",
+ "unicode": "1F918-1F3FC",
+ "digest": "e15a4898a0efca4354ac48d6b01ff0618ce8b110b1246a4f5d78e19b54658be6"
+ },
+ {
+ "name": "metal_tone3",
+ "unicode": "1F918-1F3FD",
+ "digest": "c159e8179cb1907c246b432d87c5253b914fd7cebb6ac05292c4e38eff4815b0"
+ },
+ {
+ "name": "metal_tone4",
+ "unicode": "1F918-1F3FE",
+ "digest": "a8a43a88028c97074321e3da56df1045db41ede58bf286c21d7ae90f222f2011"
+ },
+ {
+ "name": "metal_tone5",
+ "unicode": "1F918-1F3FF",
+ "digest": "e6611e826e867e2c73a8cadb138e4aa6365e3583dd229ff24b3e8f161904bf56"
+ },
+ {
+ "name": "metro",
+ "unicode": "1F687",
+ "digest": "532378cf385f9a7fafe2f5c8203e675be6d38798871f4c8e2c50498a1529f956"
+ },
+ {
+ "name": "microphone",
+ "unicode": "1F3A4",
+ "digest": "46da2b94e4dc233f640249103f09ec915aaa812cce90afe68fedb6774a27ad4b"
+ },
+ {
+ "name": "microphone2",
+ "unicode": "1F399",
+ "digest": "f9df32cd207808f67a895d3460a215d1ecc42e377907bcd64731c02b697d4f32"
+ },
+ {
+ "name": "microscope",
+ "unicode": "1F52C",
+ "digest": "79918f5fe0a39f31f270a481f4c6e00ea49fc09d64b1ae78770971293c2b1ed8"
+ },
+ {
+ "name": "middle_finger",
+ "unicode": "1F595",
+ "digest": "c6320b236a4a9593aeade511b52dd3114207e947458cb3b818c78737a505fdf6"
+ },
+ {
+ "name": "middle_finger_tone1",
+ "unicode": "1F595-1F3FB",
+ "digest": "93c7aa994856185519d576cb779bdcff3a33f7077eef98e70968125f92f02448"
+ },
+ {
+ "name": "middle_finger_tone2",
+ "unicode": "1F595-1F3FC",
+ "digest": "a0de802294717b80e08d9d30f5fd64eacb90b5b3b9d7a0c27d6226a22822597f"
+ },
+ {
+ "name": "middle_finger_tone3",
+ "unicode": "1F595-1F3FD",
+ "digest": "8bbbab07c838257416bbf8377904362c07019fca9d5abf9fd048ccf6370178da"
+ },
+ {
+ "name": "middle_finger_tone4",
+ "unicode": "1F595-1F3FE",
+ "digest": "d9eed8db540fdb669c6ae5ef168b77659660589f5ddd9b66062274d335a3ef04"
+ },
+ {
+ "name": "middle_finger_tone5",
+ "unicode": "1F595-1F3FF",
+ "digest": "0519c3298040e57db202294476df239edb9b23b44848bab296bc45eda7cf8664"
+ },
+ {
+ "name": "military_medal",
+ "unicode": "1F396",
+ "digest": "bd1da0004768f404c6bb4db85d4b748f766a77ab3edb74e709d0c0064509a043"
+ },
+ {
+ "name": "milky_way",
+ "unicode": "1F30C",
+ "digest": "598b4e641c1081bb03ce38a29f9711fc8616373216a833e4daa14fbe97a358f5"
+ },
+ {
+ "name": "minibus",
+ "unicode": "1F690",
+ "digest": "3d15791ca96349c3abb5bd5d1014b6b33b984db19609f56f5fd1e8d2fc551809"
+ },
+ {
+ "name": "minidisc",
+ "unicode": "1F4BD",
+ "digest": "83c4bfda4e0a80785fa1c3f2bbf3c15aca2bda8ea3727ce78bc4236e1e377a36"
+ },
+ {
+ "name": "mobile_phone_off",
+ "unicode": "1F4F4",
+ "digest": "cfe6dfd766b9e0b4768df25d6e943c9abc0e910ff5e5c7a8a0f425c786bbab8d"
+ },
+ {
+ "name": "money_mouth",
+ "unicode": "1F911",
+ "digest": "3ac2f9b5409e1426eef6966938ca04cf78aeffefd43f44b6c86af4af7836e22f"
+ },
+ {
+ "name": "money_with_wings",
+ "unicode": "1F4B8",
+ "digest": "f7f1fa502d2f6804169869aeb5ca7f0ea64bc2d6a0204f08875d65da4f8cb332"
+ },
+ {
+ "name": "moneybag",
+ "unicode": "1F4B0",
+ "digest": "442db49cda27360d2eb781489c9879730a6094c3267bb0a0a8687d84f8fed078"
+ },
+ {
+ "name": "monkey",
+ "unicode": "1F412",
+ "digest": "3141c971aacbadaba21f970a515e192740212be2a49fa1f5eb0fc4dc576e209f"
+ },
+ {
+ "name": "monkey_face",
+ "unicode": "1F435",
+ "digest": "e2397431d2befe44bf5298fa81d865d80722bf954113bceacc2aa98b84d856e2"
+ },
+ {
+ "name": "monorail",
+ "unicode": "1F69D",
+ "digest": "b546153200d6fbe8d65b1b34f62ff4a19b1b6a159eb1b536c5c2ecb56dab0ec9"
+ },
+ {
+ "name": "mood_bubble",
+ "unicode": "1F5F0",
+ "digest": "1df7061217e478d43ab9a87d4f351c4ca56705acd6b4e0b0bedfdece77635f1b"
+ },
+ {
+ "name": "mood_bubble_lightning",
+ "unicode": "1F5F1",
+ "digest": "4af3e4e53eaa328b0d20542ab31705a74bf9fd368cd0673b706838ce1681d3c9"
+ },
+ {
+ "name": "mood_lightning",
+ "unicode": "1F5F2",
+ "digest": "6784635e81ec722fd50a1c2a23b0f9679e4bf1b5ae2b5a01eeb995bc1f7a426f"
+ },
+ {
+ "name": "mortar_board",
+ "unicode": "1F393",
+ "digest": "cb59edb08f75c374088b65284e4d0f77b9bc9573de3e6a5127f865431011e54c"
+ },
+ {
+ "name": "mosque",
+ "unicode": "1F54C",
+ "digest": "a08ddb74342dea8f79063db6f98ba03eb08fe99481de8ce9123827ca7f17c7f3"
+ },
+ {
+ "name": "motorboat",
+ "unicode": "1F6E5",
+ "digest": "9dbea67bbe2e95dcc68c049a58f87390a44350b32308342615d75214af3d1cef"
+ },
+ {
+ "name": "motorcycle",
+ "unicode": "1F3CD",
+ "digest": "8429fb6dfeb873abdffcc179c32d4f23e91c9e6b27b06cd204fd2e83cc11189e"
+ },
+ {
+ "name": "motorway",
+ "unicode": "1F6E3",
+ "digest": "fc05a36c917637c135b0a60db8afcd58cee2b335070fe3888697f8026c9d11a5"
+ },
+ {
+ "name": "mount_fuji",
+ "unicode": "1F5FB",
+ "digest": "22bfffef033637b3c9b2fe7e539c74a659d2a49e594d2b33be894da00654d059"
+ },
+ {
+ "name": "mountain",
+ "unicode": "26F0",
+ "digest": "486cf4e9d5f3913d138fdb7878fe869b39caa3fca53876365957a89dc8f7edb8"
+ },
+ {
+ "name": "mountain_bicyclist",
+ "unicode": "1F6B5",
+ "digest": "b547b96951b6837df8ae3be1e846f15e7e2ac06d976e1fe7f1442dcc5d3a0942"
+ },
+ {
+ "name": "mountain_bicyclist_tone1",
+ "unicode": "1F6B5-1F3FB",
+ "digest": "68ce0d55163c7b89ee1d87b752ece127bb25ca9deb3421b31df549a00ac5f69d"
+ },
+ {
+ "name": "mountain_bicyclist_tone2",
+ "unicode": "1F6B5-1F3FC",
+ "digest": "5bfa82180bfb8bc4444cf301688aff02884895574a7ba66b398aaf20bde0f101"
+ },
+ {
+ "name": "mountain_bicyclist_tone3",
+ "unicode": "1F6B5-1F3FD",
+ "digest": "33cb64a792123b81a05080465a0ea1035a2cdfdab01c71f5f725a5f92251c3e8"
+ },
+ {
+ "name": "mountain_bicyclist_tone4",
+ "unicode": "1F6B5-1F3FE",
+ "digest": "9c3fa4e65dcb0ad69b963292e77c7a75853ae3c1d18a90670f81ffb65b5d020c"
+ },
+ {
+ "name": "mountain_bicyclist_tone5",
+ "unicode": "1F6B5-1F3FF",
+ "digest": "871de9e3fddb49b305e5f91000143878b0288c107a125c4e60acf2b6cf8b7f3f"
+ },
+ {
+ "name": "mountain_cableway",
+ "unicode": "1F6A0",
+ "digest": "f248ed5bf864f4a81e365b30d2825d2e6fc15a200c4ccf69e9f797341529f955"
+ },
+ {
+ "name": "mountain_railway",
+ "unicode": "1F69E",
+ "digest": "7dd08745ab56c95c3dfcebcca517ff231cef61b670cedf9d7c53f3244c34e30b"
+ },
+ {
+ "name": "mountain_snow",
+ "unicode": "1F3D4",
+ "digest": "9939aade3d4d972ba3af16fcc6cc2454978f5426e4c92838734a44db065ce0ff"
+ },
+ {
+ "name": "mouse",
+ "unicode": "1F42D",
+ "digest": "fb20b3a82f407a6316bbbac68d58018c3d5b93a9a6ae968f44ace18d1c5698d9"
+ },
+ {
+ "name": "mouse2",
+ "unicode": "1F401",
+ "digest": "87be4099523ec32440e6d091f1193a8ed90730b9fbecaafed4912585bfe7818c"
+ },
+ {
+ "name": "mouse_one",
+ "unicode": "1F5AF",
+ "digest": "e0d2055ccba489d24e0c0b6d2f22793efe48a734b0fd50f5af88f721b40665c0"
+ },
+ {
+ "name": "mouse_three_button",
+ "unicode": "1F5B1",
+ "digest": "6a5629fee01145211cc8f4e8f59c5f1e61affed38c650502213d76c7d8861b01"
+ },
+ {
+ "name": "movie_camera",
+ "unicode": "1F3A5",
+ "digest": "d6633b89a637b64d617c3032eed74bb82d3fa732dd9975486b2b5841b473808a"
+ },
+ {
+ "name": "moyai",
+ "unicode": "1F5FF",
+ "digest": "bf948c26cd98e2f5e48da363f2924a9d7c217232115a00cec372d0d5293402a8"
+ },
+ {
+ "name": "muscle",
+ "unicode": "1F4AA",
+ "digest": "c85147efb786bdea3e7d53e2edf6b827280cd9fa881661a6102a614bf5b3579f"
+ },
+ {
+ "name": "muscle_tone1",
+ "unicode": "1F4AA-1F3FB",
+ "digest": "38d071df2b25031b61f3605b03c34d2e5d3e35d29f3c4aada14be37e19750eb8"
+ },
+ {
+ "name": "muscle_tone2",
+ "unicode": "1F4AA-1F3FC",
+ "digest": "dcf11b76c8ffb58dc7e4f9ecd32a4c291d9772d51df2853d41081e041e7e0876"
+ },
+ {
+ "name": "muscle_tone3",
+ "unicode": "1F4AA-1F3FD",
+ "digest": "a3d5f8f2dbfc28f9713ee657428ea3292c47d0b22f11a51c13594be22b0f5204"
+ },
+ {
+ "name": "muscle_tone4",
+ "unicode": "1F4AA-1F3FE",
+ "digest": "eb220fc19be58d16cacc6b721e1011078b03256c0245756f251a4c2bcf50586c"
+ },
+ {
+ "name": "muscle_tone5",
+ "unicode": "1F4AA-1F3FF",
+ "digest": "4e18708cbd61eaad288f913c86ad2d45108dd4484bc35879c5dcdd075eeb09fd"
+ },
+ {
+ "name": "mushroom",
+ "unicode": "1F344",
+ "digest": "a2b252cd759244409d9a8066470059948e2c50b8cc86b59821c1c86b5190f640"
+ },
+ {
+ "name": "musical_keyboard",
+ "unicode": "1F3B9",
+ "digest": "dcb3e84d27bfe373e5ea7ede457908de52002f0fd6105e9f3f5525c54d2a43dd"
+ },
+ {
+ "name": "musical_note",
+ "unicode": "1F3B5",
+ "digest": "76a0f598f8e251a9dab44f2e14f2b7a6fb0c0c351e0f37862c8c99d380f1c261"
+ },
+ {
+ "name": "musical_score",
+ "unicode": "1F3BC",
+ "digest": "a132c6b35236005b45c830a42fa97b454d3061c14991c6320f34807f10ba6a4a"
+ },
+ {
+ "name": "mute",
+ "unicode": "1F507",
+ "digest": "73a99b7f9e00f92cab78cd304dee4e893a112c3a6f2285c13d44916ea547458e"
+ },
+ {
+ "name": "nail_care",
+ "unicode": "1F485",
+ "digest": "62f721d3610d1647dba4b3f53cd4f2bc4180dae298314c2cca2a6a8ab1664525"
+ },
+ {
+ "name": "nail_care_tone1",
+ "unicode": "1F485-1F3FB",
+ "digest": "11b82ed2e6b6619c9b74702fdacfb0ddc91310191c8b89f355c7c69a72673f8f"
+ },
+ {
+ "name": "nail_care_tone2",
+ "unicode": "1F485-1F3FC",
+ "digest": "5195c76bccb9149d9080347d785dae2cce947bada5b198fae8c23e42f5553154"
+ },
+ {
+ "name": "nail_care_tone3",
+ "unicode": "1F485-1F3FD",
+ "digest": "50eab0bf825c5e00db07a3f5ad26b1bb221f54efb5c55549f392b2f5aec09e5a"
+ },
+ {
+ "name": "nail_care_tone4",
+ "unicode": "1F485-1F3FE",
+ "digest": "d05a9ccfad02191c89e4cbd00aa48fdaf908c0de6681f4a587d500be448e528f"
+ },
+ {
+ "name": "nail_care_tone5",
+ "unicode": "1F485-1F3FF",
+ "digest": "62466354dcf6717a8b9e942ca2c5ad15a26aa815c213e3b01faba9a2e302ecdd"
+ },
+ {
+ "name": "name_badge",
+ "unicode": "1F4DB",
+ "digest": "0a1cb0f7d489d3356a4d3e01f9faf78449d82d8ec4595c8639a55c3606c97c40"
+ },
+ {
+ "name": "necktie",
+ "unicode": "1F454",
+ "digest": "029e1140391ef559a9316021c2db94f05653751fdf9d8f366446467a70fee6df"
+ },
+ {
+ "name": "negative_squared_cross_mark",
+ "unicode": "274E",
+ "digest": "0ba0e705fdeac99edd712db31a8846320b9d2cf53c9cb4d4bcfd22ba4e1488ea"
+ },
+ {
+ "name": "nerd",
+ "unicode": "1F913",
+ "digest": "94efd551700aae8909b8dd7a78a54a33e070d24b2e0a10534353645084614e98"
+ },
+ {
+ "name": "network",
+ "unicode": "1F5A7",
+ "digest": "1dbaa54deeb2328fd8a3f044e450c97ac3ff39627c598bb2f4312d677482ee06"
+ },
+ {
+ "name": "neutral_face",
+ "unicode": "1F610",
+ "digest": "df01da8501e1f588049c8ed66e504e9abcce83f74ce5790f4d3dc547408f77ee"
+ },
+ {
+ "name": "new",
+ "unicode": "1F195",
+ "digest": "24e80abd29750d8b297335cdd4751b6250bb820560cf0392a6cc8783d34db63a"
+ },
+ {
+ "name": "new_moon",
+ "unicode": "1F311",
+ "digest": "2d697e431eac53d6e1ea367b5da03c15fc535cd7e8c214f801fe595b768a8e11"
+ },
+ {
+ "name": "new_moon_with_face",
+ "unicode": "1F31A",
+ "digest": "ea469a4668ded071f35e5898ae229fdb5d02b0730ce233169b83e22f81292baa"
+ },
+ {
+ "name": "newspaper",
+ "unicode": "1F4F0",
+ "digest": "0aaf6747a43fb60cd15e6e64ca0eccaade331b376c6fe6712fd5e8294e9868cc"
+ },
+ {
+ "name": "newspaper2",
+ "unicode": "1F5DE",
+ "digest": "0ca6b5850091f23295c970815a8e64a52e3c3dae492029ecb1e0726c2693f9bf"
+ },
+ {
+ "name": "ng",
+ "unicode": "1F196",
+ "digest": "4994c9b795033ed788e98c4af571a1dffe28c0a1479e3b42dcae21bb08381b5f"
+ },
+ {
+ "name": "night_with_stars",
+ "unicode": "1F303",
+ "digest": "56bb4a59a897c1836ee1a49cc99f468891b790b0f8bce203c201c13bb7b8ae9a"
+ },
+ {
+ "name": "nine",
+ "unicode": "0039-20E3",
+ "digest": "7e3644a98cb6417a351530c9ce6b368e637a22c847a8c04133897dc1c5d7419f"
+ },
+ {
+ "name": "no_bell",
+ "unicode": "1F515",
+ "digest": "f4fb42836132000101624fecef8b9358736a0fc76beae460e6986aaa479204fd"
+ },
+ {
+ "name": "no_bicycles",
+ "unicode": "1F6B3",
+ "digest": "b3c258bea7d6988640e3348598c03c97632ca00a11cbf0352995b801ff4a296b"
+ },
+ {
+ "name": "no_entry",
+ "unicode": "26D4",
+ "digest": "ac807d54092efdc3aea417790a7d0c50b59800c9ea49b37f1aec6d2e453c5f6d"
+ },
+ {
+ "name": "no_entry_sign",
+ "unicode": "1F6AB",
+ "digest": "5a17d677ec1c7595a7970a1cbe0d20909341b30d3ab31471ced590f51fff1ff7"
+ },
+ {
+ "name": "no_good",
+ "unicode": "1F645",
+ "digest": "8ce921e5e13e1203cf43fdc3e7c5ec1fb2a1f9ff79f21539cff542c80af2e5fe"
+ },
+ {
+ "name": "no_good_tone1",
+ "unicode": "1F645-1F3FB",
+ "digest": "aab4d354aaac06e8348eb354487c6381e475b44651cb2716660904a36c47a1b6"
+ },
+ {
+ "name": "no_good_tone2",
+ "unicode": "1F645-1F3FC",
+ "digest": "8fb66b1a7b8f72062794281294515d47471a8c59de300b99d656c3412ca19d64"
+ },
+ {
+ "name": "no_good_tone3",
+ "unicode": "1F645-1F3FD",
+ "digest": "aeecf73fb9dca24b4002db2802fc9b5a483644c49f834c19f143d4e56ec46c1a"
+ },
+ {
+ "name": "no_good_tone4",
+ "unicode": "1F645-1F3FE",
+ "digest": "fadeb23307d5ccabbf08c848cf81c66c05b152aa32b85f86061caf14760f8eb9"
+ },
+ {
+ "name": "no_good_tone5",
+ "unicode": "1F645-1F3FF",
+ "digest": "cf26d5d6463d0febf4e1f08e343308742ffe0811cfc30c459b87d4cc812f5d04"
+ },
+ {
+ "name": "no_mobile_phones",
+ "unicode": "1F4F5",
+ "digest": "3b4ead88beca33f1e303d0a45268849be7aaaff7830b761732c7a5afc5a2de3a"
+ },
+ {
+ "name": "no_mouth",
+ "unicode": "1F636",
+ "digest": "2af81a3e07a8b7827a1e58f6f5036ccff2f6e7b0027a4f934c9fa34c6a780963"
+ },
+ {
+ "name": "no_pedestrians",
+ "unicode": "1F6B7",
+ "digest": "9f9ed90bb8f9964fa8cb0048fc092ecc0612a1994c98d19ef0b5a58607888476"
+ },
+ {
+ "name": "no_smoking",
+ "unicode": "1F6AD",
+ "digest": "fb90290ff5c917b7307a97c8ba793d20c61042525cf2f7bfd4cd2a7878aeefa5"
+ },
+ {
+ "name": "non-potable_water",
+ "unicode": "1F6B1",
+ "digest": "c4ddca2ab1a97260e9b2c2aa33fb03455c0e8174541c3a9416fc44143a3ee567"
+ },
+ {
+ "name": "nose",
+ "unicode": "1F443",
+ "digest": "308e28b15b7f734f6f184ae367789d7cf258656b24861cf8d5935127d810aa3f"
+ },
+ {
+ "name": "nose_tone1",
+ "unicode": "1F443-1F3FB",
+ "digest": "392d24b38ac3edc2d7b83945a60bbe9115a6a97658d8af35281a7cbef79449e8"
+ },
+ {
+ "name": "nose_tone2",
+ "unicode": "1F443-1F3FC",
+ "digest": "409a790339c405770492e49fdb0b5ba34087c27e2f9018ecd845ab078e61476a"
+ },
+ {
+ "name": "nose_tone3",
+ "unicode": "1F443-1F3FD",
+ "digest": "92b52b479a935f31e460257d809c531edad1a6bb4583ad18233b12c4e45202fe"
+ },
+ {
+ "name": "nose_tone4",
+ "unicode": "1F443-1F3FE",
+ "digest": "78ad2e857792e86cded6ba5620f634f7d1f79a92c82c266e48fab9bd73df3688"
+ },
+ {
+ "name": "nose_tone5",
+ "unicode": "1F443-1F3FF",
+ "digest": "dbef6813c1965d3e93f70f33f118f9950130af21c622cea97ea215a36b4fa73f"
+ },
+ {
+ "name": "note",
+ "unicode": "1F5C9",
+ "digest": "073660fdaa02ecf98d04f61f8d65d6cc447ccae3825fccaff19a2c99ebba52af"
+ },
+ {
+ "name": "note_empty",
+ "unicode": "1F5C6",
+ "digest": "06b56eeaca6349bbcf1020bea98f937450a7e086db65cd5d7497748e0fb607be"
+ },
+ {
+ "name": "notebook",
+ "unicode": "1F4D3",
+ "digest": "64bd4a3e7ca7b22fc704c7b7bd4d13540c16bc69b9d8dd76e69e6ad573ab3823"
+ },
+ {
+ "name": "notebook_with_decorative_cover",
+ "unicode": "1F4D4",
+ "digest": "4b45f28fbde1be5c214a6bc2413abc91db02bccd86f74c21b7f4a4da8b75a46f"
+ },
+ {
+ "name": "notepad",
+ "unicode": "1F5CA",
+ "digest": "85069e2d13540886457368a57295072aec44c7137d9223bfcf908ce1f0e5124e"
+ },
+ {
+ "name": "notepad_empty",
+ "unicode": "1F5C7",
+ "digest": "8be5053e74c13d8220917c5aee1f4afdecb001612886438f283b0c2a0fecf6af"
+ },
+ {
+ "name": "notepad_spiral",
+ "unicode": "1F5D2",
+ "digest": "c181b6c1cc6063ec1848e46cbbf1d8b890c53b59cdc5218311ce06889570e727"
+ },
+ {
+ "name": "notes",
+ "unicode": "1F3B6",
+ "digest": "bf3868386e17eac40ac7fbabea027042027ff061daafe406c869cdd8ce94641d"
+ },
+ {
+ "name": "nut_and_bolt",
+ "unicode": "1F529",
+ "digest": "fdb9d7408202fad7a52ff21608042c08c3b0beb195999fff233df36a29dc9e96"
+ },
+ {
+ "name": "o",
+ "unicode": "2B55",
+ "digest": "8e119dba4130bd33b3ee5c862fb4fa5a691173911ffee51cb9359fee3398e330"
+ },
+ {
+ "name": "o2",
+ "unicode": "1F17E",
+ "digest": "00d751124c25633611055bd61e74fc3f3d1779f0d09e1e707837686f613367b4"
+ },
+ {
+ "name": "ocean",
+ "unicode": "1F30A",
+ "digest": "9b1fbfd2a64f417d0c2cb91085b29a12d14e15844bc21798bdee938bb7bf6222"
+ },
+ {
+ "name": "octopus",
+ "unicode": "1F419",
+ "digest": "3fdfbc02f47ad434bdeb7f3a15cd4e8f8118ee1cd754627e358f1c2f4616f5e3"
+ },
+ {
+ "name": "oden",
+ "unicode": "1F362",
+ "digest": "afed1c5166943e5803602ffacc67652e3b29ee4222a6c36aba2daf88bd21ad3c"
+ },
+ {
+ "name": "office",
+ "unicode": "1F3E2",
+ "digest": "dc1836ef152d88fd628df18db770594f5dbc8d7f20d6ce982588b25b78b19c92"
+ },
+ {
+ "name": "oil",
+ "unicode": "1F6E2",
+ "digest": "f8b7626cb09e229203105b9c8c7f3fbb38c0650021092fc50115ad517248644a"
+ },
+ {
+ "name": "ok",
+ "unicode": "1F197",
+ "digest": "6b05bbab4a7104541c2f4bce553884d17ae0ad07589b19d6b53b6949c14f2269"
+ },
+ {
+ "name": "ok_hand",
+ "unicode": "1F44C",
+ "digest": "9981f32ef200b011a10f6bfa2066c41b6b5e7bcd6c3c21647980b640bc1fa93b"
+ },
+ {
+ "name": "ok_hand_tone1",
+ "unicode": "1F44C-1F3FB",
+ "digest": "e5933a9b64b03ce0634f15f02ff7b6424530dbdc0e283461e0c9992d0c2ca2ad"
+ },
+ {
+ "name": "ok_hand_tone2",
+ "unicode": "1F44C-1F3FC",
+ "digest": "4c04741c9f2c8731da8df3015e9aae00061a01848c2d22aab1e9853c271deed3"
+ },
+ {
+ "name": "ok_hand_tone3",
+ "unicode": "1F44C-1F3FD",
+ "digest": "216dc5a72f9e34bbb7b39f680c388bd5b52abf9b41b843342e53e285b7933076"
+ },
+ {
+ "name": "ok_hand_tone4",
+ "unicode": "1F44C-1F3FE",
+ "digest": "7139de7ec9d5a962cf87b9fbbeef3a53aa482bb840ab3b64d8d0da81bdc19886"
+ },
+ {
+ "name": "ok_hand_tone5",
+ "unicode": "1F44C-1F3FF",
+ "digest": "e18b0a1bc5d970cc63466bd6da6e9f855db37d1eada3230d19f600c1f5a402a3"
+ },
+ {
+ "name": "ok_woman",
+ "unicode": "1F646",
+ "digest": "3b2fa732d9c9addb056f136192428e99d805d4cb1c7dab724fd552c7e93197e4"
+ },
+ {
+ "name": "ok_woman_tone1",
+ "unicode": "1F646-1F3FB",
+ "digest": "017aca3797701b043a44f22e67dcad8b531a3ca14e629ae0d2fbc601ed3e49cb"
+ },
+ {
+ "name": "ok_woman_tone2",
+ "unicode": "1F646-1F3FC",
+ "digest": "036bed032bc5a616668775cda0d5640c810e2836aa28009c8e8bf2b487259c59"
+ },
+ {
+ "name": "ok_woman_tone3",
+ "unicode": "1F646-1F3FD",
+ "digest": "d9a4414caddda43d1a36828cfbecce5f2b7e5c1b67b4a47991b2ae0a34cf7ab7"
+ },
+ {
+ "name": "ok_woman_tone4",
+ "unicode": "1F646-1F3FE",
+ "digest": "942e1b9aa495c4c4de0804e4d4348422201299d649e5d65829ba4a308880df1c"
+ },
+ {
+ "name": "ok_woman_tone5",
+ "unicode": "1F646-1F3FF",
+ "digest": "e8d0fb5b999d5d63404493aa505b5af2260c76001023431d5e788773d0a9e2de"
+ },
+ {
+ "name": "older_man",
+ "unicode": "1F474",
+ "digest": "620f763325827acbeb9d57798ef55d87827d0dfc77b84d942e25bc5057f2cbfe"
+ },
+ {
+ "name": "older_man_tone1",
+ "unicode": "1F474-1F3FB",
+ "digest": "e0f35c12362eae503d1c30a345c3a4978196d351d8a1eb9d5f107c60ea4bbf52"
+ },
+ {
+ "name": "older_man_tone2",
+ "unicode": "1F474-1F3FC",
+ "digest": "671766ce9fa47c3fa009d4f138344c87d73032a1c38e48614c663f8ea5d0f673"
+ },
+ {
+ "name": "older_man_tone3",
+ "unicode": "1F474-1F3FD",
+ "digest": "6ff4885ef8c416b8970780a691fef74c8d89421ab11e0aa8c522c33e1c67fbe8"
+ },
+ {
+ "name": "older_man_tone4",
+ "unicode": "1F474-1F3FE",
+ "digest": "0ae7d4e316dcd4d27a5a6cdaabab88a4f992bd1b75f6ceaeb5b906ed1eb5269c"
+ },
+ {
+ "name": "older_man_tone5",
+ "unicode": "1F474-1F3FF",
+ "digest": "abe2757bd5e35f30d2a6daec09637ea5382a46d14d239b77282e9bf874229b57"
+ },
+ {
+ "name": "older_woman",
+ "unicode": "1F475",
+ "digest": "3ed599443eed25399aac999fc234c9e97f8fb6ec567e37a553c26e01021b097c"
+ },
+ {
+ "name": "older_woman_tone1",
+ "unicode": "1F475-1F3FB",
+ "digest": "7421c5dba67cfd1eeabb2fa8faf4aa0d615d23f191cf7d7c0ad9c1fa884edfda"
+ },
+ {
+ "name": "older_woman_tone2",
+ "unicode": "1F475-1F3FC",
+ "digest": "65edeef25648ac7f8be535df06af1286441691fa15176e99a6e83fc779aa2cde"
+ },
+ {
+ "name": "older_woman_tone3",
+ "unicode": "1F475-1F3FD",
+ "digest": "5d27bbcc5796227a9caec1c7612d3f691055655b96f7303e420839463d76c269"
+ },
+ {
+ "name": "older_woman_tone4",
+ "unicode": "1F475-1F3FE",
+ "digest": "75b858e910175fc0233503d672120fd43ac035ba3fd2052fbb44df39f6e3695c"
+ },
+ {
+ "name": "older_woman_tone5",
+ "unicode": "1F475-1F3FF",
+ "digest": "9da1cf10a605c470877d7f4a840f99344b1ec2e7b1ec7db61e930cde77025e3b"
+ },
+ {
+ "name": "om_symbol",
+ "unicode": "1F549",
+ "digest": "c8c1c9d445b1fc50a627b71bee21fba978e04532e4685ec032a0174f51fc12bb"
+ },
+ {
+ "name": "on",
+ "unicode": "1F51B",
+ "digest": "08e1159a68d3334a87ffa75b9e70826cb557d0f73a2c1d08f4c3d60476ecacc8"
+ },
+ {
+ "name": "oncoming_automobile",
+ "unicode": "1F698",
+ "digest": "6bff7f40fe223df6d16c7512532b8aa6f83e8c13e1007b63eb9aabf774c1a322"
+ },
+ {
+ "name": "oncoming_bus",
+ "unicode": "1F68D",
+ "digest": "127a357fcd96ce4b9ab11c3dba95d8ff811bab193dd8ba38efb7067a44752ce8"
+ },
+ {
+ "name": "oncoming_police_car",
+ "unicode": "1F694",
+ "digest": "57cb70e05e70c1f68ab42259f307ed9782c2b9d6e35d2dff2895aa23d7eb6b04"
+ },
+ {
+ "name": "oncoming_taxi",
+ "unicode": "1F696",
+ "digest": "174967ae4c3d5881d2408c71c020f704e933190af4caef5d2908e9ac382f35ea"
+ },
+ {
+ "name": "one",
+ "unicode": "0031-20E3",
+ "digest": "113b9d87c3e37c9c54e49cecccbfc40c15fb97fd03a51505df85e48b78702b2b"
+ },
+ {
+ "name": "open_file_folder",
+ "unicode": "1F4C2",
+ "digest": "def93715203aed464211798d773732895a19389a94a2e7ed43e7f229b2aab7da"
+ },
+ {
+ "name": "open_hands",
+ "unicode": "1F450",
+ "digest": "7c60a37ae11727c998908199b8709e52593b931843aef942f37b306b1edca12a"
+ },
+ {
+ "name": "open_hands_tone1",
+ "unicode": "1F450-1F3FB",
+ "digest": "09ffa9b3f28fc56a71e4e711bdfc87ce1a56721229377e71f1c00224523f8b9b"
+ },
+ {
+ "name": "open_hands_tone2",
+ "unicode": "1F450-1F3FC",
+ "digest": "21ecaba9f086bcb7eb07c17c2b2621bcd1ca28c57f79032d5e0eba356494cc85"
+ },
+ {
+ "name": "open_hands_tone3",
+ "unicode": "1F450-1F3FD",
+ "digest": "c7dbb8c44f78f7793b202ec215fee42b7e1e555d659fbf402383500217b89656"
+ },
+ {
+ "name": "open_hands_tone4",
+ "unicode": "1F450-1F3FE",
+ "digest": "867451d42492ab2277687447f421f744530b9ea057312326353fec39c94b18fd"
+ },
+ {
+ "name": "open_hands_tone5",
+ "unicode": "1F450-1F3FF",
+ "digest": "56335506cf68e29150cb68d7ebbb4a92aed390018966669a8144d20ae0d6cfe3"
+ },
+ {
+ "name": "open_mouth",
+ "unicode": "1F62E",
+ "digest": "f05fdf998e8b5c0b00ebd8b5ab17a67f5c0a45275f31a201af74e8ab0c2f7ba9"
+ },
+ {
+ "name": "ophiuchus",
+ "unicode": "26CE",
+ "digest": "98c61bb0c36d60c476d42d5e074297662e8d141dcab7004a5bd63c359eed3b84"
+ },
+ {
+ "name": "optical_disk",
+ "unicode": "1F5B8",
+ "digest": "df8c10028d29d65f144a6b789d1c3294e7b3293554c4c30d28d72dc7ba8d9a5d"
+ },
+ {
+ "name": "orange_book",
+ "unicode": "1F4D9",
+ "digest": "86d150ea3d62183ab7dfe2851cf7f4d1ae769b7ecbb1987b0f463e639e429598"
+ },
+ {
+ "name": "orthodox_cross",
+ "unicode": "2626",
+ "digest": "9c861285ca6d699cd2c72b6df44ec2b1e64138152f19c66e32df1ce770ff2e83"
+ },
+ {
+ "name": "outbox_tray",
+ "unicode": "1F4E4",
+ "digest": "b6a6015d5d7d528af485de23ff4518dc35408def1cc49bc6c9b01d880d613985"
+ },
+ {
+ "name": "ox",
+ "unicode": "1F402",
+ "digest": "cbcfe5c8c4d6b939e24e18e610785f171bb9410441e02c2eeb1bceb0a6246daf"
+ },
+ {
+ "name": "package",
+ "unicode": "1F4E6",
+ "digest": "4023cffce85384217a73609f457aec013876e689c44bcfff0bcc35f3e4e1ab00"
+ },
+ {
+ "name": "page",
+ "unicode": "1F5CF",
+ "digest": "cc745056525f59d9128d1d03b14770376bb09ab64b8ef4ac994ab7f38efd4783"
+ },
+ {
+ "name": "page_facing_up",
+ "unicode": "1F4C4",
+ "digest": "71a0872bf1b13c58746f9b41655227c75be107ab6083c0dce13cb16444af22e7"
+ },
+ {
+ "name": "page_with_curl",
+ "unicode": "1F4C3",
+ "digest": "cb4210464faea946c7b07db7067c7fc98920f778cf57721388f5362942ba3029"
+ },
+ {
+ "name": "pager",
+ "unicode": "1F4DF",
+ "digest": "209dbdc19aa650ecacc0569e17a9123c9a1e39df59c9b4120f3b0888b63cd6f1"
+ },
+ {
+ "name": "pages",
+ "unicode": "1F5D0",
+ "digest": "05bd47b78f089389356d9d839c736843f56b959ab4277056606ffcbb013390bc"
+ },
+ {
+ "name": "paintbrush",
+ "unicode": "1F58C",
+ "digest": "73eb33184f5f495d6c2699fafc1a8680069f82a70fbe519290c3a2ce30d1aee9"
+ },
+ {
+ "name": "palm_tree",
+ "unicode": "1F334",
+ "digest": "1589ff4b1b87296edc0118e4aa67b3b504ed85a5b8d47e7d0c3e309d0bbf8cd6"
+ },
+ {
+ "name": "panda_face",
+ "unicode": "1F43C",
+ "digest": "050ee87892f56ff485f460bc6c3846d98a0ca7083d2cf0b8ab24772b672273f2"
+ },
+ {
+ "name": "paperclip",
+ "unicode": "1F4CE",
+ "digest": "1463607a59345973f009fa53a719e2264b95743560adb99737bef29b1d133a95"
+ },
+ {
+ "name": "paperclips",
+ "unicode": "1F587",
+ "digest": "7071e031f4a100c3cb3573fbfa375360043f0276289a0818f2ffaf71b3580040"
+ },
+ {
+ "name": "park",
+ "unicode": "1F3DE",
+ "digest": "d257f0f1b1a0134573f80ba1a5f522a91c320ee7f93a1cb64877c077e7e19b50"
+ },
+ {
+ "name": "parking",
+ "unicode": "1F17F",
+ "digest": "e1d2cfd1c57ea85003ca4df066cbba4e506bf6c4d6c790e27b2f78ad8443fabf"
+ },
+ {
+ "name": "part_alternation_mark",
+ "unicode": "303D",
+ "digest": "b3cc2e803b255e858417345ba6ba52a1c22f511b483fec11b5d68c4432f759b6"
+ },
+ {
+ "name": "partly_sunny",
+ "unicode": "26C5",
+ "digest": "484990f5e1a3b14c731e7bd4b0b4a1c10cd5fb54ac7cf2751f40c8bf59d7e2b4"
+ },
+ {
+ "name": "passport_control",
+ "unicode": "1F6C2",
+ "digest": "224e8ef60d4d6587721727555de324948fb5b6c1cb5cc4b546960983d1ec85c4"
+ },
+ {
+ "name": "pause_button",
+ "unicode": "23F8",
+ "digest": "edd605ffaa39a7905ed0958b7cc69f00f5b271e579198d2df1746ad1b3648272"
+ },
+ {
+ "name": "peace",
+ "unicode": "262E",
+ "digest": "e0ee8a5c9fb18d5db6841b21527ed8fd955abdff9ffdb7b2684dca22107015fc"
+ },
+ {
+ "name": "peach",
+ "unicode": "1F351",
+ "digest": "a3f4fd5ff02e0a03104ab54456ee1a7521858ee68443856ee10e0972e5b6aaa5"
+ },
+ {
+ "name": "pear",
+ "unicode": "1F350",
+ "digest": "7a7a72568d53677cd1fff4d9e58e63327a742fa16d22a2bef03b4a6fa378d3b3"
+ },
+ {
+ "name": "pen_ballpoint",
+ "unicode": "1F58A",
+ "digest": "6becdc6f622c774bb09b7e7592bba2123ecccc9de32a35f0b18b50d7d54109cb"
+ },
+ {
+ "name": "pen_fountain",
+ "unicode": "1F58B",
+ "digest": "8c78cf0c2bd1d5e309d2d3356ff207e3fc76ca18dd6b90762cb62f6afbc95c6a"
+ },
+ {
+ "name": "pencil",
+ "unicode": "1F4DD",
+ "digest": "62b7ee5d9352114d09ee6f2c9a4c5e8b79f775a6c509e82ddfcdd61e13716249"
+ },
+ {
+ "name": "pencil2",
+ "unicode": "270F",
+ "digest": "aa2c572772187fee1f9125bb0950f5ce8a61f7dd2647258c40b4077ee5feb498"
+ },
+ {
+ "name": "pencil3",
+ "unicode": "1F589",
+ "digest": "52c1ba1228917eb491ac1745a495e0fdafba6b985a81caba250f71d1f94c725c"
+ },
+ {
+ "name": "penguin",
+ "unicode": "1F427",
+ "digest": "095de34b3f6a2521a342c21f5f2551a0092bf47429801c15b7bbf0913924f412"
+ },
+ {
+ "name": "pennant_black",
+ "unicode": "1F3F2",
+ "digest": "cd3c33bfc3c7fbe84b98d2d481d56a7bf5488ff94afadd8b5a0e454768b80269"
+ },
+ {
+ "name": "pennant_white",
+ "unicode": "1F3F1",
+ "digest": "818b1be73540f2cfeb1c514e1ee75d18715af317f0db817d9ae081b9ea33d4b0"
+ },
+ {
+ "name": "pensive",
+ "unicode": "1F614",
+ "digest": "2d9e7f1eed14dcc86674cec78e992567a40d0f223fc67d722b91eebcd1251269"
+ },
+ {
+ "name": "performing_arts",
+ "unicode": "1F3AD",
+ "digest": "a202755bab6427433975589bb8b63e61e5d7f55c6242676d8000e91eedabc55e"
+ },
+ {
+ "name": "persevere",
+ "unicode": "1F623",
+ "digest": "686ef3fc70ce8294d02a764ebd75b69f25cca6bff6b92e7905130366d22f6d8a"
+ },
+ {
+ "name": "person_frowning",
+ "unicode": "1F64D",
+ "digest": "16e8fbf22c0b4c237d0d45202fa32d1ebd04760a5b6975c9c9b477321ccb0e12"
+ },
+ {
+ "name": "person_frowning_tone1",
+ "unicode": "1F64D-1F3FB",
+ "digest": "a143b865976ce3cf307db854cfd1ca58c3832df0eee5e9b0ab307cf4f24ba3db"
+ },
+ {
+ "name": "person_frowning_tone2",
+ "unicode": "1F64D-1F3FC",
+ "digest": "4e7050d8a38019ba2293f66b9930e6a7e35dacf3b3bc9431edb586a0d9ea8054"
+ },
+ {
+ "name": "person_frowning_tone3",
+ "unicode": "1F64D-1F3FD",
+ "digest": "0750015d3ac1b5954d31e36cd59c70b6ed9f4df698082484b7ac59eb0b9964b0"
+ },
+ {
+ "name": "person_frowning_tone4",
+ "unicode": "1F64D-1F3FE",
+ "digest": "18d6cc92d0990624218d38d6eeed60bccb371d0fc9f1c889e9476b3b0c44b5e8"
+ },
+ {
+ "name": "person_frowning_tone5",
+ "unicode": "1F64D-1F3FF",
+ "digest": "4a898199cbaf083d37511f51d8a1d2560b7a20c62a1b09087831da7010fbd093"
+ },
+ {
+ "name": "person_with_blond_hair",
+ "unicode": "1F471",
+ "digest": "67d95a0801c65f62db55fa80ab35dec65c239601a44bf5f5902e4645f126770e"
+ },
+ {
+ "name": "person_with_blond_hair_tone1",
+ "unicode": "1F471-1F3FB",
+ "digest": "e79717bfe30a26eafc082a75fa7547d8f2ad3c123fb2d75a95e75f0ce7ecbd0c"
+ },
+ {
+ "name": "person_with_blond_hair_tone2",
+ "unicode": "1F471-1F3FC",
+ "digest": "c4a1961c292149ab6e1fd54a7894398599bf855de97a05ee4e836a86a400deb3"
+ },
+ {
+ "name": "person_with_blond_hair_tone3",
+ "unicode": "1F471-1F3FD",
+ "digest": "e2707d0cf778bee5b72d861ec76430eb1cf9f9820f066ee6327574d5697f445e"
+ },
+ {
+ "name": "person_with_blond_hair_tone4",
+ "unicode": "1F471-1F3FE",
+ "digest": "94da43f0b12ef4a98dabec096ff1184b0a9b5b6ee55824d257e5112cc7e88730"
+ },
+ {
+ "name": "person_with_blond_hair_tone5",
+ "unicode": "1F471-1F3FF",
+ "digest": "9e096a210ea720d32bc6a7005cd77f8b314ccf817fc3060da2e1796de39e9d60"
+ },
+ {
+ "name": "person_with_pouting_face",
+ "unicode": "1F64E",
+ "digest": "8c3199a422250d2db9a163156191ed2c6697d7f31699e2efe19e05ca26e5d225"
+ },
+ {
+ "name": "person_with_pouting_face_tone1",
+ "unicode": "1F64E-1F3FB",
+ "digest": "3e1f09bbf607381c992739ea92dd35cbd26b1bbc705a7d21b7c3156f50e9d8b3"
+ },
+ {
+ "name": "person_with_pouting_face_tone2",
+ "unicode": "1F64E-1F3FC",
+ "digest": "b5fc1cf3fdc5ff01105ee2452db90baa6a52c1e42f3795b2836c3e35197ece1f"
+ },
+ {
+ "name": "person_with_pouting_face_tone3",
+ "unicode": "1F64E-1F3FD",
+ "digest": "e8ec2539c458a8283c8c1050634c432b6363f3e64b68ba4c977994782f09b564"
+ },
+ {
+ "name": "person_with_pouting_face_tone4",
+ "unicode": "1F64E-1F3FE",
+ "digest": "5cab7a29699decd45682583446c2bf56ddcd69cd16e14db661b526a4076dfa17"
+ },
+ {
+ "name": "person_with_pouting_face_tone5",
+ "unicode": "1F64E-1F3FF",
+ "digest": "3caebd3626fd77d849859d1c99a747f80a2b59bfa5c1854494f1ce0485539a94"
+ },
+ {
+ "name": "pick",
+ "unicode": "26CF",
+ "digest": "24a3e8f592435b97272e6d134ea5503dce3012811659c4aadbad4e45d9fba679"
+ },
+ {
+ "name": "pig",
+ "unicode": "1F437",
+ "digest": "50b55fc74e8f6c89c6e04609381c99a660748908f0ef015f5da37089678ad0c3"
+ },
+ {
+ "name": "pig2",
+ "unicode": "1F416",
+ "digest": "e8189fb678608e8b9d69e11d2566f9a4765cbdff99ec8e66df30c7a2dabf742f"
+ },
+ {
+ "name": "pig_nose",
+ "unicode": "1F43D",
+ "digest": "7e299cb49a771884f5065c68733a5a1fe354a54cff009127230177f1717af4a5"
+ },
+ {
+ "name": "pill",
+ "unicode": "1F48A",
+ "digest": "53ae3379cc6721744979122569f157a5a13aa6b48e081a89f17b2d90134efe9e"
+ },
+ {
+ "name": "pineapple",
+ "unicode": "1F34D",
+ "digest": "ceda8ffa4a41594f28a4e69d03f8a1daeb2ba20740f0b8c56447cae833eea035"
+ },
+ {
+ "name": "ping_pong",
+ "unicode": "1F3D3",
+ "digest": "dd2a84716c93410a285ff759bfbc2dc31a10f90b203c7a657b908e5949e89a39"
+ },
+ {
+ "name": "piracy",
+ "unicode": "1F572",
+ "digest": "f42955ba75c598392e5e258be49968d858c876e0d6e7aa9dc795f7e8cff42be9"
+ },
+ {
+ "name": "pisces",
+ "unicode": "2653",
+ "digest": "75f11b9a094196b54a242420362fa7c0aeba7cfc497b187e1aaaba96d93684a7"
+ },
+ {
+ "name": "pizza",
+ "unicode": "1F355",
+ "digest": "ac94ae1c034f7b854ce2a483e1c219d101a84336f5065342f4824ff32ba705c4"
+ },
+ {
+ "name": "place_of_worship",
+ "unicode": "1F6D0",
+ "digest": "4fabc307b7e35f94288f6d53985485662a4814b11a9a382f0a3873d41b1290d3"
+ },
+ {
+ "name": "play_pause",
+ "unicode": "23EF",
+ "digest": "d69e8cdec33447283cf65d343b986115e27681d781b721db7894e5c587ca18ad"
+ },
+ {
+ "name": "point_down",
+ "unicode": "1F447",
+ "digest": "685f46a643be7f3033896e59a822f87d61ce50db6969bcdbacc743215a96bb7a"
+ },
+ {
+ "name": "point_down_tone1",
+ "unicode": "1F447-1F3FB",
+ "digest": "d3dd2608fe17d5649c960fcf8dbdb68466908d80fa349b7947b457da2a27ebb1"
+ },
+ {
+ "name": "point_down_tone2",
+ "unicode": "1F447-1F3FC",
+ "digest": "67ab236a14f6d63abcdb26433a66a183d223186c21ebc9f978fab50165ebe271"
+ },
+ {
+ "name": "point_down_tone3",
+ "unicode": "1F447-1F3FD",
+ "digest": "c8a2368f2cedb5bbb5cc0195b97fbf3787747637bf6e77bdc9a4edf4a3f22a04"
+ },
+ {
+ "name": "point_down_tone4",
+ "unicode": "1F447-1F3FE",
+ "digest": "6a92eab3bc8f950fa423e690f54a352887bda92f01e91c62eb3f3a9544c10cd8"
+ },
+ {
+ "name": "point_down_tone5",
+ "unicode": "1F447-1F3FF",
+ "digest": "6ad329f156414f421d6f8cf5e2a68d34b7a41f90d80e8e66b15bcbd3788126c7"
+ },
+ {
+ "name": "point_left",
+ "unicode": "1F448",
+ "digest": "cb520d6bba4c2b3bd7911315c9efce3261d048ff090437d7e24c9c5a7255043e"
+ },
+ {
+ "name": "point_left_tone1",
+ "unicode": "1F448-1F3FB",
+ "digest": "81813901bdaa8d261277f79aff9e9a21beb80a5855899941820b25f70786ec21"
+ },
+ {
+ "name": "point_left_tone2",
+ "unicode": "1F448-1F3FC",
+ "digest": "ecdc3dea0d644290aa7e0dab758c215822482a482ba35d825a33152453593c1e"
+ },
+ {
+ "name": "point_left_tone3",
+ "unicode": "1F448-1F3FD",
+ "digest": "84e73b6a37755016271c255eba164f349dbd2a2badf5d9ac1c6f4cbfcae589f0"
+ },
+ {
+ "name": "point_left_tone4",
+ "unicode": "1F448-1F3FE",
+ "digest": "d16800499b6c6ede94256796b1de8a8f723879f636849856b3bd8b7a092b5576"
+ },
+ {
+ "name": "point_left_tone5",
+ "unicode": "1F448-1F3FF",
+ "digest": "18b7108066cebf2d4090f29e595a2f01db94bd210f3b1d61dc269ec249a749b9"
+ },
+ {
+ "name": "point_right",
+ "unicode": "1F449",
+ "digest": "866180bf31e92de32aba336d5b5ce81773a29cdaadada1d93c944cf9ad9783bc"
+ },
+ {
+ "name": "point_right_tone1",
+ "unicode": "1F449-1F3FB",
+ "digest": "ebe2e4bf6bd46a5798b9a845a4ed055911c4fe58dbeacc4d39d6ea63e28e7cc9"
+ },
+ {
+ "name": "point_right_tone2",
+ "unicode": "1F449-1F3FC",
+ "digest": "b638662a67b1c6adde4f5abc789aae010b178404cdd1b71fcc982cdf8307c655"
+ },
+ {
+ "name": "point_right_tone3",
+ "unicode": "1F449-1F3FD",
+ "digest": "32c6ca2f992416ab2c36672dfbc1c0de8f102c77a13496dd8d63736a7b0261d2"
+ },
+ {
+ "name": "point_right_tone4",
+ "unicode": "1F449-1F3FE",
+ "digest": "89bd6828e9b82408a3829d49fa43332e2599f7d10bc6e5b14b750ef03267b173"
+ },
+ {
+ "name": "point_right_tone5",
+ "unicode": "1F449-1F3FF",
+ "digest": "390525048a12b0efa22de550c800e439b0deaad03f1f31155d179aef093354af"
+ },
+ {
+ "name": "point_up",
+ "unicode": "261D",
+ "digest": "31b5ca1303c1afabe1db322b24f73b23f3568c87a364f61c82f6e0c858c090e9"
+ },
+ {
+ "name": "point_up_2",
+ "unicode": "1F446",
+ "digest": "55c237054aa347c9847f3f3f577eb755db55dfcf793aa7de0f8f868574d70e8f"
+ },
+ {
+ "name": "point_up_2_tone1",
+ "unicode": "1F446-1F3FB",
+ "digest": "dc07e7732d973de96ae3b08b14c19e20b6c1aea7f5a30e7198679b750422e914"
+ },
+ {
+ "name": "point_up_2_tone2",
+ "unicode": "1F446-1F3FC",
+ "digest": "af2211fc4a1bd51d1e76f7bc43a6fa87bdd24e4295c52fdbdb01c1ca670a6cd7"
+ },
+ {
+ "name": "point_up_2_tone3",
+ "unicode": "1F446-1F3FD",
+ "digest": "917701169b3fb3e1b6e14a68e9572b25998ef2e38abac9ad8cf30100f8ea0dac"
+ },
+ {
+ "name": "point_up_2_tone4",
+ "unicode": "1F446-1F3FE",
+ "digest": "20843904764c6c3e55792cce0c55c76f72b97788c5229cad655ebf1f2873b439"
+ },
+ {
+ "name": "point_up_2_tone5",
+ "unicode": "1F446-1F3FF",
+ "digest": "1d0cca546027c717da50f90da65757af46fe7cd4e397da9b8e203446f707208d"
+ },
+ {
+ "name": "point_up_tone1",
+ "unicode": "261D-1F3FB",
+ "digest": "5ede60379dee23166c6b834d73da8b55268e330f67058843b8a3705dca6ed71a"
+ },
+ {
+ "name": "point_up_tone2",
+ "unicode": "261D-1F3FC",
+ "digest": "c94a15ef848d410aa5d32b8d0e453b59682fde6f39e6705cbb81cf0829833a81"
+ },
+ {
+ "name": "point_up_tone3",
+ "unicode": "261D-1F3FD",
+ "digest": "d319ce72876d97a3b1d4bc7c0679e546a983f02145d723a0da5ed0b73a51cfe7"
+ },
+ {
+ "name": "point_up_tone4",
+ "unicode": "261D-1F3FE",
+ "digest": "9171a27f86f27fd144347a17153fb56e30bd32e67a8f10f8c1f32a40cad4e009"
+ },
+ {
+ "name": "point_up_tone5",
+ "unicode": "261D-1F3FF",
+ "digest": "a894f87da4c3d33d5e6e74d003a33ec60c453db6507fe05d22235f807ead27d6"
+ },
+ {
+ "name": "police_car",
+ "unicode": "1F693",
+ "digest": "7999869cb75be404fc34942b6f9d8e84fa7e259aa892a1e8e1652a5f02cceea6"
+ },
+ {
+ "name": "poodle",
+ "unicode": "1F429",
+ "digest": "8a568d8688bf19b440b7c1b49fcfe6672b8f75af0031d89ab6212623430acadb"
+ },
+ {
+ "name": "poop",
+ "unicode": "1F4A9",
+ "digest": "140ce75a015ede5e764873e0ae9a56e7b2af333eddca0fe2796b14545c620258"
+ },
+ {
+ "name": "popcorn",
+ "unicode": "1F37F",
+ "digest": "12264cb16fca9317e3ba8d5924a2c8f15f790e36d2f29e7b12aaaf77e1beb73d"
+ },
+ {
+ "name": "post_office",
+ "unicode": "1F3E3",
+ "digest": "5e2d896cd646a2eecd5596af9e44ca1fa2745de5cedaf0f6d193b8243201c6cc"
+ },
+ {
+ "name": "postal_horn",
+ "unicode": "1F4EF",
+ "digest": "339aa61fa1567a1d159bb8204d15db889fbb6cc1106f6e1991b4a184d1bc1fc7"
+ },
+ {
+ "name": "postbox",
+ "unicode": "1F4EE",
+ "digest": "ef1a6543fccb9f1009cc3782c51883e51167721a0b49e8ba21e8e6049b216906"
+ },
+ {
+ "name": "potable_water",
+ "unicode": "1F6B0",
+ "digest": "4a2379835660dfa8b6780d662a10d1effab710f471eb9b5e6ade4772ba7e5aeb"
+ },
+ {
+ "name": "pouch",
+ "unicode": "1F45D",
+ "digest": "cbd47ec1a65f5c642773d8ea2e7e57f7041a2d7ed9df05fbdd7bc8743c6dece6"
+ },
+ {
+ "name": "poultry_leg",
+ "unicode": "1F357",
+ "digest": "d416e9464bd58073bd3e32eb06c0da96905609f47b9d667acdc0810e94237584"
+ },
+ {
+ "name": "pound",
+ "unicode": "1F4B7",
+ "digest": "1ac491bb8a91613b2b1faaac4e7b4bc794d2abef69ac79de17d54c824c3ef826"
+ },
+ {
+ "name": "pouting_cat",
+ "unicode": "1F63E",
+ "digest": "ba28d75401d5bb98773acd35aaf173356bae4d5a5520a226559478138364ebdf"
+ },
+ {
+ "name": "pray",
+ "unicode": "1F64F",
+ "digest": "fb0df9c1566014bd2df2a1afd59366b896f20c03ca3516e02e4be44ea556c8ea"
+ },
+ {
+ "name": "pray_tone1",
+ "unicode": "1F64F-1F3FB",
+ "digest": "c6d8cb46e65ad13a92e85f97e018176fd89513f23e899e15d1ad09e3b4009f4b"
+ },
+ {
+ "name": "pray_tone2",
+ "unicode": "1F64F-1F3FC",
+ "digest": "2cd68cbe1ba3254f173ec8136af79cae64873bd0f20480158c3e6babd5a1a442"
+ },
+ {
+ "name": "pray_tone3",
+ "unicode": "1F64F-1F3FD",
+ "digest": "d2e81863f74a87b96335fb108e7b206f28ed18185362ab4d42a3b0523801398b"
+ },
+ {
+ "name": "pray_tone4",
+ "unicode": "1F64F-1F3FE",
+ "digest": "ad1b91254b101d872325c325ebd1f2a6257cfe22e83de88e29dd16ffac191979"
+ },
+ {
+ "name": "pray_tone5",
+ "unicode": "1F64F-1F3FF",
+ "digest": "23f40a11321decbdc6a1d274b9ad571041d261d364d13d1063c306e73ad52254"
+ },
+ {
+ "name": "prayer_beads",
+ "unicode": "1F4FF",
+ "digest": "cb6f8700154f75749cf2642a25c03e255dc18428baf8b57f6bd807c92b83e28d"
+ },
+ {
+ "name": "princess",
+ "unicode": "1F478",
+ "digest": "47b93eb52d757c3c000d9760391ecb942776d883b28050d833fa11612483d8ee"
+ },
+ {
+ "name": "princess_tone1",
+ "unicode": "1F478-1F3FB",
+ "digest": "1e4073c2abdf51a61a1a85a3e063541fe96e9b9ec36ec6f7fb9c98deeb230869"
+ },
+ {
+ "name": "princess_tone2",
+ "unicode": "1F478-1F3FC",
+ "digest": "6a0a5dc447cd887798f908c15972e7a12d28d81f168b92bcb105786ac253bea0"
+ },
+ {
+ "name": "princess_tone3",
+ "unicode": "1F478-1F3FD",
+ "digest": "2f08d22fdfc7a7d66fcd87ae716b811f43077f5bb17fef87f5b7e2aa93700d70"
+ },
+ {
+ "name": "princess_tone4",
+ "unicode": "1F478-1F3FE",
+ "digest": "02129211bf7bf7ff6de35913b7069aee151532d878b8c4f7e24c012e5b09d4b4"
+ },
+ {
+ "name": "princess_tone5",
+ "unicode": "1F478-1F3FF",
+ "digest": "d676f103600b69dbfdb469469a77b9d561ec460ff862befa58ab30ddc909c9f7"
+ },
+ {
+ "name": "printer",
+ "unicode": "1F5A8",
+ "digest": "c44402c87071f8d31d3997abab53ab9f8f7c11434e747380928814ceb6b0a417"
+ },
+ {
+ "name": "prohibited",
+ "unicode": "1F6C7",
+ "digest": "bc6cdea2269a0ec39576d98dc4cda2bd9efa4dc330dde870148c6a85ad9cc63f"
+ },
+ {
+ "name": "projector",
+ "unicode": "1F4FD",
+ "digest": "fc361282f367926254c08150b02cb8fda7fa8d2c9c939d9360c78bf19a4f982e"
+ },
+ {
+ "name": "punch",
+ "unicode": "1F44A",
+ "digest": "5759db1d7093744c74b840bbb4761fb025d6633f8fa539bcb35dcf54fc05ceb6"
+ },
+ {
+ "name": "punch_tone1",
+ "unicode": "1F44A-1F3FB",
+ "digest": "793b3fa2a43c23b2c1e1b48b86ae35e8c4024cd065fac0a0a5ada87cb78d6de3"
+ },
+ {
+ "name": "punch_tone2",
+ "unicode": "1F44A-1F3FC",
+ "digest": "6fc2467e99982ab00b0c352c6f7793d34faf17b16a0312082c9bd1f0709e3938"
+ },
+ {
+ "name": "punch_tone3",
+ "unicode": "1F44A-1F3FD",
+ "digest": "bf747b29952550c5b4d3807b9ed85b5e5d4bcc3265b0e214791f7db547f861fb"
+ },
+ {
+ "name": "punch_tone4",
+ "unicode": "1F44A-1F3FE",
+ "digest": "3b6c0ccb682552f32d6744c438e3af04a1732c67a74bcafb14c723cf526fed87"
+ },
+ {
+ "name": "punch_tone5",
+ "unicode": "1F44A-1F3FF",
+ "digest": "945bae1aa3587cd1dc57d1ec4da18c67a59e0e7150dcc8735e5357b4ea1234ac"
+ },
+ {
+ "name": "purple_heart",
+ "unicode": "1F49C",
+ "digest": "e0eb886e74f22d40d059ff3a089d472af53c6c53de380f428cca140dfd046345"
+ },
+ {
+ "name": "purse",
+ "unicode": "1F45B",
+ "digest": "67d82ff9a4d76148b9d98538d4b786f880058a556e650ec3f93e1632aa42aaa7"
+ },
+ {
+ "name": "pushpin",
+ "unicode": "1F4CC",
+ "digest": "c4de129d5d8744caffeb2f499fcc0bc6b551843938f8166ffecd0de00bda66e3"
+ },
+ {
+ "name": "pushpin_black",
+ "unicode": "1F588",
+ "digest": "80ebac74edb9e8e1f8a219b32a676d318ed73b359cd8193b91b493d775307f63"
+ },
+ {
+ "name": "put_litter_in_its_place",
+ "unicode": "1F6AE",
+ "digest": "b26d3b68bd62d30ecfe75cfaf309a7a0f91e92db0aa18b0b97b97baf0609d4e6"
+ },
+ {
+ "name": "question",
+ "unicode": "2753",
+ "digest": "258e3169bae177fb0f01ed5f9b933f7f02dd2673e12a316af44a0c3729a78a2c"
+ },
+ {
+ "name": "rabbit",
+ "unicode": "1F430",
+ "digest": "9817a7454aeda77d28f63eb13c0dc0a6d9e6c9abe3dcf538b4b3477e494cddb6"
+ },
+ {
+ "name": "rabbit2",
+ "unicode": "1F407",
+ "digest": "67ba57a31b0768a2118faabdcb088f96f1441e1132397f65b6937d523ff7dabb"
+ },
+ {
+ "name": "race_car",
+ "unicode": "1F3CE",
+ "digest": "2e9828e3884c79ad7e9e1173d3470790f3f56cfa08ef4e38deff45db0728c66c"
+ },
+ {
+ "name": "racehorse",
+ "unicode": "1F40E",
+ "digest": "36aa3c7123ee7e15600657166032b21b8edeb192cf6d3ada39b5c65001f7fc40"
+ },
+ {
+ "name": "radio",
+ "unicode": "1F4FB",
+ "digest": "b1403f9a883405b909208f52c9474c2d3923681ea0b02609a6e9dc12460319a5"
+ },
+ {
+ "name": "radio_button",
+ "unicode": "1F518",
+ "digest": "9bcdac17b3620331a32f9bb876812231a701eb5a7f696e7d875f877ab92159fc"
+ },
+ {
+ "name": "radioactive",
+ "unicode": "2622",
+ "digest": "5ad8e8594617c0153672a76421deb836e05c6098020c33af3f975f8fcfe216e4"
+ },
+ {
+ "name": "rage",
+ "unicode": "1F621",
+ "digest": "02ac70551fc51478884c133b29539cae58b463c760db38c0aeec1bdf5b282312"
+ },
+ {
+ "name": "railway_car",
+ "unicode": "1F683",
+ "digest": "8490e2ecf94c7c1d1e22fea0d80cc18a49648741009e51984f583b17bbd022e2"
+ },
+ {
+ "name": "railway_track",
+ "unicode": "1F6E4",
+ "digest": "63ee881cc775d5b2711082b6c96ab44d5204c5d390afd6d8ee97e52aeeaa5e5e"
+ },
+ {
+ "name": "rainbow",
+ "unicode": "1F308",
+ "digest": "bbd8ecc8d0737948969a3539d2d202e599404e509f1a21bdbb0a0c41c2540522"
+ },
+ {
+ "name": "raised_hand",
+ "unicode": "270B",
+ "digest": "4192881a0d613b4fcb19b1c2d8b83aadee6f0b12170721c8dd7b1ccef6540199"
+ },
+ {
+ "name": "raised_hand_tone1",
+ "unicode": "270B-1F3FB",
+ "digest": "df2e046c99dceb9184c50a777b403d72bfb25ff473d6a4e20bb9a731db64ed8d"
+ },
+ {
+ "name": "raised_hand_tone2",
+ "unicode": "270B-1F3FC",
+ "digest": "ed179299a1c397cd51cf6067d6795d71a3831d35e1ec9eacbf0286c8992c1e7a"
+ },
+ {
+ "name": "raised_hand_tone3",
+ "unicode": "270B-1F3FD",
+ "digest": "cacbd0ddef65bc01a41bd921ea159f8cd89050309b10f15780d6199f79434a54"
+ },
+ {
+ "name": "raised_hand_tone4",
+ "unicode": "270B-1F3FE",
+ "digest": "04c934c7a55b83bcfa7f3880fc1f6aa0f188090c37b9670e6775a512a1cf59e9"
+ },
+ {
+ "name": "raised_hand_tone5",
+ "unicode": "270B-1F3FF",
+ "digest": "da0c4283b7b19861237c023234c6db28045b8f5a5971acb015733e08e2940e86"
+ },
+ {
+ "name": "raised_hands",
+ "unicode": "1F64C",
+ "digest": "308e475f38558e73bd66e28693d77478caa5bca4360cffaffc2a97b5858c56ba"
+ },
+ {
+ "name": "raised_hands_tone1",
+ "unicode": "1F64C-1F3FB",
+ "digest": "e39b9bc49dccc127e44f543e98961fcf5bcd44d6e216741bcd10ec3667263c84"
+ },
+ {
+ "name": "raised_hands_tone2",
+ "unicode": "1F64C-1F3FC",
+ "digest": "f376ab13071ffdc11888ec221ef5b4de546ca0f60bd9ae30bf3da4066c220462"
+ },
+ {
+ "name": "raised_hands_tone3",
+ "unicode": "1F64C-1F3FD",
+ "digest": "67694325a43e629c00fa9bd2ff7e19f84f216b2855ae2cf097762dfa7aca25e6"
+ },
+ {
+ "name": "raised_hands_tone4",
+ "unicode": "1F64C-1F3FE",
+ "digest": "a2254fe75a0770708916a4ddd5db4420221c6ea9db9f74068d14eadfc0f3772c"
+ },
+ {
+ "name": "raised_hands_tone5",
+ "unicode": "1F64C-1F3FF",
+ "digest": "bd7c9897cefb454ccdc46027bf56d6587565bdd345d7d0f081b7b671a53f6c99"
+ },
+ {
+ "name": "raising_hand",
+ "unicode": "1F64B",
+ "digest": "d57178fc77e9fa140682634da35f9ab12a65d9b4c506b7cd8a9697f1b5910bdb"
+ },
+ {
+ "name": "raising_hand_tone1",
+ "unicode": "1F64B-1F3FB",
+ "digest": "f46b34361ef79743f3187d6860182bbe1ae411031db7fe5c0f7292fa472b9c16"
+ },
+ {
+ "name": "raising_hand_tone2",
+ "unicode": "1F64B-1F3FC",
+ "digest": "20b85a2ebca150b2020a04b41d34884c78c22f42c251e2b9d23fd3724574143b"
+ },
+ {
+ "name": "raising_hand_tone3",
+ "unicode": "1F64B-1F3FD",
+ "digest": "5e0401b528c2b8edff766d39cdcedbe9abebe4c940df7a36ace61f59c08d508a"
+ },
+ {
+ "name": "raising_hand_tone4",
+ "unicode": "1F64B-1F3FE",
+ "digest": "e4f5624264269ad09cde207cd7d4eb0fd46de816880daeec457ac8cd51cc1b7b"
+ },
+ {
+ "name": "raising_hand_tone5",
+ "unicode": "1F64B-1F3FF",
+ "digest": "eb34b6c037bee5bbc4222f6aab421aa785f527ebf1b5e971769e5102244d60e1"
+ },
+ {
+ "name": "ram",
+ "unicode": "1F40F",
+ "digest": "b71950d7a286a4c4909c5ec7c35211c2a5c20b6bad341bd863c6a85c4bcf9c80"
+ },
+ {
+ "name": "ramen",
+ "unicode": "1F35C",
+ "digest": "7dd185b24852b577913edc78647cd53b27d42e225fde29aa2f3aba25c980b5c4"
+ },
+ {
+ "name": "rat",
+ "unicode": "1F400",
+ "digest": "7a10d9ba5ee1010d421d9cf73d7966507302a69617a32fe9f1a00d57a31f7bd7"
+ },
+ {
+ "name": "record_button",
+ "unicode": "23FA",
+ "digest": "c2ba672994ad0f99d7fdc449f3fee45a2dca68a58f9fe95825b38465a30ef44e"
+ },
+ {
+ "name": "recycle",
+ "unicode": "267B",
+ "digest": "74a54ed62a40dfbdcace1f08b085658a77d45c62570273927ad270bf9a8a2f4d"
+ },
+ {
+ "name": "red_car",
+ "unicode": "1F697",
+ "digest": "558730d6418aa5d85b73af58c8041efd12cff906e26ea47c50963f66d33d6eb8"
+ },
+ {
+ "name": "red_circle",
+ "unicode": "1F534",
+ "digest": "9dcf0132f6f2cc81702f0e3b15b37984e8439796705bf98f68ba449b3dfa5307"
+ },
+ {
+ "name": "registered",
+ "unicode": "00AE",
+ "digest": "ed924107384461aabb4924c401c6c087ffa047bc2ef735823e7c2be67804707c"
+ },
+ {
+ "name": "relaxed",
+ "unicode": "263A",
+ "digest": "65072f7b9bfaaa92b8a0ed012dffe2cfd2efa3748264aaf450aa31ba6bd44045"
+ },
+ {
+ "name": "relieved",
+ "unicode": "1F60C",
+ "digest": "1f2c7ae6a9d74a112de89403be6eca3d8155d70395e7fce51032fc961f235c7d"
+ },
+ {
+ "name": "reminder_ribbon",
+ "unicode": "1F397",
+ "digest": "e4a2afc7dce40589657f7043ba8acc9638fd4117252278233ea89f84cddad387"
+ },
+ {
+ "name": "repeat",
+ "unicode": "1F501",
+ "digest": "27b6dad9215e58e24c607a39dbf398ecf66ccb692c81e08eb2f5f4912db30522"
+ },
+ {
+ "name": "repeat_one",
+ "unicode": "1F502",
+ "digest": "052d13f2b08eaf70b31252aa78f95d06fbe22c58945c19381b13cbeb1c855651"
+ },
+ {
+ "name": "restroom",
+ "unicode": "1F6BB",
+ "digest": "b77fbc4247c241362e5ef9e6eb58b1b437aa9d16b65886cec0c55ceb55c1440e"
+ },
+ {
+ "name": "revolving_hearts",
+ "unicode": "1F49E",
+ "digest": "2b8925d3e78df2dba8534252fe60bf03285346f6b3697be7668bd568e6d85931"
+ },
+ {
+ "name": "rewind",
+ "unicode": "23EA",
+ "digest": "91a95b26d12ca76111556096f4d96484c9f1d7e1b20ccff5a3291b36e529a6d1"
+ },
+ {
+ "name": "ribbon",
+ "unicode": "1F380",
+ "digest": "9c0296d8c2baa84c99347c431bf79b288d98b5f17b1ce7605ad7ce1da265d5aa"
+ },
+ {
+ "name": "rice",
+ "unicode": "1F35A",
+ "digest": "e34849496a79e71ae4700df94f2a54895bf6de758a92edeae33fe78295a3ba21"
+ },
+ {
+ "name": "rice_ball",
+ "unicode": "1F359",
+ "digest": "52df5da8b0edbdeb56d66e0f30ad4549abdd81c064f7269d920dcac66a3df2e4"
+ },
+ {
+ "name": "rice_cracker",
+ "unicode": "1F358",
+ "digest": "d55f8f9d807f4619eb243c510938067a7417a64bd9435b05dfeb2a36fdb2b6a0"
+ },
+ {
+ "name": "rice_scene",
+ "unicode": "1F391",
+ "digest": "482d854d8d30edfc1ecd48a4ce476e6498606321405bf5a0b4ff74489a092af8"
+ },
+ {
+ "name": "right_speaker",
+ "unicode": "1F568",
+ "digest": "d268bb84be863c0884620dfc6d2a764b0c7466d2f9810549b138e21ac70add4e"
+ },
+ {
+ "name": "right_speaker_one",
+ "unicode": "1F569",
+ "digest": "5b92daa87bdf6ee15e798bec382a2ee885f4e6e77a68a3f626adcfe4c782b375"
+ },
+ {
+ "name": "right_speaker_three",
+ "unicode": "1F56A",
+ "digest": "4d00b720a65bd0f4c3682b290b1976ec2388d6ae61225398f4e70556ae9e5f80"
+ },
+ {
+ "name": "ring",
+ "unicode": "1F48D",
+ "digest": "ae2a93e7895b9b89f5a39f01d356ffed988f219ef8b658a56c55285826a4533b"
+ },
+ {
+ "name": "ringing_bell",
+ "unicode": "1F56D",
+ "digest": "d71ab7fa937fc4af507b5b07ea58a4f31e875d9e8304ef2b850d7cebe0e9cd66"
+ },
+ {
+ "name": "robot",
+ "unicode": "1F916",
+ "digest": "cc0e363774b86e21a5b2cea7f7af85bca9e92c124ebcd39c6067c125048baa60"
+ },
+ {
+ "name": "rocket",
+ "unicode": "1F680",
+ "digest": "65d8bd005ceac41904237b7a8c5f55f16713a55d971522f0bbe63a1d548e515d"
+ },
+ {
+ "name": "roller_coaster",
+ "unicode": "1F3A2",
+ "digest": "907baab1f3d7becf3f8a3b1264642b395bd73b4af49e23058b3abb5c69e9106a"
+ },
+ {
+ "name": "rolling_eyes",
+ "unicode": "1F644",
+ "digest": "f596f203030b6c9bd743848512aa3fc7919447020d35ae5c2bf13ccb16fa2dbe"
+ },
+ {
+ "name": "rooster",
+ "unicode": "1F413",
+ "digest": "6cefdaa45631ed8c9480e15f578c793d95af81b42687164fd7900eee325ccf07"
+ },
+ {
+ "name": "rose",
+ "unicode": "1F339",
+ "digest": "584909a4a2ece625c688f8479a39692bb8e816b692e6eb7dfd40cb045259b1b2"
+ },
+ {
+ "name": "rosette",
+ "unicode": "1F3F5",
+ "digest": "0ce3b85ca05124ab99d57ebc9aa17bb246ee614d2fcda1ef62bf42ac7e616148"
+ },
+ {
+ "name": "rosette_black",
+ "unicode": "1F3F6",
+ "digest": "ae8675891c88f9d98463d35178445950c39b0deb0f0e8b3f341228a6e0d0e477"
+ },
+ {
+ "name": "rotating_light",
+ "unicode": "1F6A8",
+ "digest": "369e069e0bfecc7413e75f4015e9c1de527a33c7cce3f6c2b4adb60a0d9d338c"
+ },
+ {
+ "name": "round_pushpin",
+ "unicode": "1F4CD",
+ "digest": "1bc5fe5a90a6e56ea00246f1b008a0e0cce0d77c226dc0300bf9a2804b543877"
+ },
+ {
+ "name": "rowboat",
+ "unicode": "1F6A3",
+ "digest": "c10e09bf8be8b1a8ef3113edd9327126d6a4644f3bc81c7ada2922851e4d1cfb"
+ },
+ {
+ "name": "rowboat_tone1",
+ "unicode": "1F6A3-1F3FB",
+ "digest": "a84fc1b30d1a284dcd3899dc4de8f11e7b65c258528eb41c7dbf8f82425fee12"
+ },
+ {
+ "name": "rowboat_tone2",
+ "unicode": "1F6A3-1F3FC",
+ "digest": "85f001430a2ad607a15901f7c2dcf8381471f42d6cc0775e76a2ff1f457151c1"
+ },
+ {
+ "name": "rowboat_tone3",
+ "unicode": "1F6A3-1F3FD",
+ "digest": "adf8b1e45a46a13f3db40c29df0312216558e9d0c615aa46a8e913cee5003a81"
+ },
+ {
+ "name": "rowboat_tone4",
+ "unicode": "1F6A3-1F3FE",
+ "digest": "05482749ec40bdf02e53fc42d316c51f4f3ed643f21e8fc16b81930e4a884bda"
+ },
+ {
+ "name": "rowboat_tone5",
+ "unicode": "1F6A3-1F3FF",
+ "digest": "d4bb337d948996d4a23d87f99988f02fc207815b862082ffd2eef5f0c1016aa9"
+ },
+ {
+ "name": "rugby_football",
+ "unicode": "1F3C9",
+ "digest": "e14aebbded78d4a5e9b4028f79a8ca840d02798c6758cb9e926e992e2a35a4f3"
+ },
+ {
+ "name": "runner",
+ "unicode": "1F3C3",
+ "digest": "58a884f06d37b0ce78197bebcd3f0e102dd90022ebd86ec70a2ef5a5cdf9683b"
+ },
+ {
+ "name": "runner_tone1",
+ "unicode": "1F3C3-1F3FB",
+ "digest": "65f1633d1517803de23686d2dbcc75a5787874266db4981138ccdbe4badc773c"
+ },
+ {
+ "name": "runner_tone2",
+ "unicode": "1F3C3-1F3FC",
+ "digest": "2bc81f3fb77445cdc75c34806ab0ce912bacfe47f63b5d2011a4f5d370cf7064"
+ },
+ {
+ "name": "runner_tone3",
+ "unicode": "1F3C3-1F3FD",
+ "digest": "beaf5f254cba2991fdd0c38ce2ddd1b4c1110e15b2b7bc026d32f162e295c4ef"
+ },
+ {
+ "name": "runner_tone4",
+ "unicode": "1F3C3-1F3FE",
+ "digest": "21d531ba9b3d13747ad636b8f7a6f184c974bf61d9f529975a64f9629263c407"
+ },
+ {
+ "name": "runner_tone5",
+ "unicode": "1F3C3-1F3FF",
+ "digest": "b02a5bcc58cc45f8219262ec44c77764172fd8f2624d9122ded4a5a5db04c0ed"
+ },
+ {
+ "name": "running_shirt_with_sash",
+ "unicode": "1F3BD",
+ "digest": "431bed35f4a55175bf99af769e74a81e8650c6ab34af6ecddaa1417ff7e437e6"
+ },
+ {
+ "name": "sa",
+ "unicode": "1F202",
+ "digest": "a47a480631f874e8a2cd69b5d513f90a1e81a96bfa2f6025bf244a82baca3656"
+ },
+ {
+ "name": "sagittarius",
+ "unicode": "2650",
+ "digest": "14871e6681c35e4a63a0b19613f77b3674d00cb78d06975e02ca29e61b5cea8c"
+ },
+ {
+ "name": "sailboat",
+ "unicode": "26F5",
+ "digest": "6f742dde6c180a174b771aa3942b558e98a3dc1eb212dd31add86c5fa5620865"
+ },
+ {
+ "name": "sake",
+ "unicode": "1F376",
+ "digest": "aa1392790c805950779dde7778292c937f8c1aaecb522876171d5ee542ec51f8"
+ },
+ {
+ "name": "sandal",
+ "unicode": "1F461",
+ "digest": "14f1e9003a6acd90a55f23c48ed87a758fca586f2e0b0edc4dc9d1deef9eb067"
+ },
+ {
+ "name": "santa",
+ "unicode": "1F385",
+ "digest": "12feddd84eb49ce30ae68d4f93d66e2c0dd11297a4d1275c9a50d4f35bea83a9"
+ },
+ {
+ "name": "santa_tone1",
+ "unicode": "1F385-1F3FB",
+ "digest": "a75813770efe27d5b4c80ad892d0c796d88d1a0dbb1bd02d5f68882d7abad479"
+ },
+ {
+ "name": "santa_tone2",
+ "unicode": "1F385-1F3FC",
+ "digest": "90f8072fdde5f4a275cbd1902d6c94689d453b1bee0336213dc9d6f7e1d038e1"
+ },
+ {
+ "name": "santa_tone3",
+ "unicode": "1F385-1F3FD",
+ "digest": "0973053e7b77d268080126a50b95b45429630e5d49f62210e7b71840794c7dc5"
+ },
+ {
+ "name": "santa_tone4",
+ "unicode": "1F385-1F3FE",
+ "digest": "5cd49c0d199a42846b400b3c1244d448ed6fe5ce993d379817cb2a5f7c0b609b"
+ },
+ {
+ "name": "santa_tone5",
+ "unicode": "1F385-1F3FF",
+ "digest": "a54c36dfa99b39549fb1d3dd7f0021a7aee28112960172ed466dacc67961c525"
+ },
+ {
+ "name": "satellite",
+ "unicode": "1F4E1",
+ "digest": "3b9797c8161526edce0bd8e9b8563055166f9307761c367ab3e2ad7645b6dee0"
+ },
+ {
+ "name": "satellite_orbital",
+ "unicode": "1F6F0",
+ "digest": "104b135e3736a4bcfd51a42dadb53bf3e00d7f85d77a94bcb86c6704fbfacd01"
+ },
+ {
+ "name": "saxophone",
+ "unicode": "1F3B7",
+ "digest": "1090da174ce8aa4f7d35025f65d5ac235e09310abde998d2a725ef3a989a2b75"
+ },
+ {
+ "name": "scales",
+ "unicode": "2696",
+ "digest": "b2984caa182b691a33650344708f47c61d6d319fd067760d7594c2ef60c1e27b"
+ },
+ {
+ "name": "school",
+ "unicode": "1F3EB",
+ "digest": "caf35260dc465a833521e4a0034201978fed41bbf72cd770756b3340c60e8a0c"
+ },
+ {
+ "name": "school_satchel",
+ "unicode": "1F392",
+ "digest": "a89a2cc46d24d57c2d6b95ed7a56ed829ae2f97b9e6201b2d5adc78c2b78518b"
+ },
+ {
+ "name": "scissors",
+ "unicode": "2702",
+ "digest": "a4e91127ac83acf5ebc64fbeca768cbbf24f2f0a484861c9c8104bee377b97ae"
+ },
+ {
+ "name": "scorpion",
+ "unicode": "1F982",
+ "digest": "a090a96731bc1171b054b51abec4c9b36faa62708fd51ac48277ccf5e55d9d12"
+ },
+ {
+ "name": "scorpius",
+ "unicode": "264F",
+ "digest": "1ad9bc1030a8f58f3f3223bac52c954cc7a0350805a9df7a42a26972c3b74728"
+ },
+ {
+ "name": "scream",
+ "unicode": "1F631",
+ "digest": "75d613786737ee9c0a74da7394b9ae190eacc7182164627ad8205ac64e4cc09a"
+ },
+ {
+ "name": "scream_cat",
+ "unicode": "1F640",
+ "digest": "eee04ff27c2c6b57d698cb87b0af8064ba8313ffc13aa090e38cd5aa8c3d2f76"
+ },
+ {
+ "name": "scroll",
+ "unicode": "1F4DC",
+ "digest": "b8205847649e3ce6b946f1d1da972ed015adde3841c62971b8169235f4b41c1f"
+ },
+ {
+ "name": "seat",
+ "unicode": "1F4BA",
+ "digest": "054c4db0bc8939e9dd951a3f73e9ae4b3c31652784f4d304b509c2bd32f98e31"
+ },
+ {
+ "name": "secret",
+ "unicode": "3299",
+ "digest": "77daef6e5c91d55228781ddec954a7089d1851297ec81daef6e813cd22915b5e"
+ },
+ {
+ "name": "see_no_evil",
+ "unicode": "1F648",
+ "digest": "aa5883fe605aeaa172d16640b8347580f9cb7d85a596da1b13955f27b0b79297"
+ },
+ {
+ "name": "seedling",
+ "unicode": "1F331",
+ "digest": "a75ec929402de1e653fd6bc89e5be2f92fe5fe52f39e4b6c290eae3c59172b56"
+ },
+ {
+ "name": "seven",
+ "unicode": "0037-20E3",
+ "digest": "c6a34020f6bb25871164fad44302a45c5bffced87f51dfbb816c2985ad7f6a1c"
+ },
+ {
+ "name": "shamrock",
+ "unicode": "2618",
+ "digest": "530e6b987ecb9bcbf0d6e0e11bd075e7949873c784da4f9e1e1b47efd37e5058"
+ },
+ {
+ "name": "shaved_ice",
+ "unicode": "1F367",
+ "digest": "fc22c3568f6be56771e83fd0e67b7eb3750041304d5d4979d3ec417f5201230e"
+ },
+ {
+ "name": "sheep",
+ "unicode": "1F411",
+ "digest": "3e3656b82784164ca02c5d775db7245260f0119d2c1d35ba552a6dc75ef02544"
+ },
+ {
+ "name": "shell",
+ "unicode": "1F41A",
+ "digest": "ff2f4f574b61bffd85c63bc2315c80d3cbcaba37a7c15a1f00783d312bd441d4"
+ },
+ {
+ "name": "shield",
+ "unicode": "1F6E1",
+ "digest": "062aec4a325da7b637c5710846c7e7319229be49b7e59f50428442a7ef725d60"
+ },
+ {
+ "name": "shinto_shrine",
+ "unicode": "26E9",
+ "digest": "9768fe94142a7dc169703d3707b203f285a546455e29fe2bbf185d44f160d6d0"
+ },
+ {
+ "name": "ship",
+ "unicode": "1F6A2",
+ "digest": "f8d5b0c8ec66287b732d9171ac1913be02efb656de11501213a207d8a6c801e1"
+ },
+ {
+ "name": "shirt",
+ "unicode": "1F455",
+ "digest": "e2e72c323f3bfaea02e8cf52201aa144dc56ec0f25ec97d5f04ee6c2ee99104e"
+ },
+ {
+ "name": "shopping_bags",
+ "unicode": "1F6CD",
+ "digest": "0194ba540c47e4fc6403be2df68f785d56810efc2dc011dfbf700f3778cb704a"
+ },
+ {
+ "name": "shower",
+ "unicode": "1F6BF",
+ "digest": "c945120182392510348de9a957c2b77a4645d118691298a2ad660dafa62a859c"
+ },
+ {
+ "name": "signal_strength",
+ "unicode": "1F4F6",
+ "digest": "7876ed9d602e1be746ca0629f072d85668d1f9715e9135745e803bdf89819a3c"
+ },
+ {
+ "name": "six",
+ "unicode": "0036-20E3",
+ "digest": "b409f23b73e46393c7a814442816b5880c38ef12a7feb5505e71276c195e8ca9"
+ },
+ {
+ "name": "six_pointed_star",
+ "unicode": "1F52F",
+ "digest": "4bc294dcbf4185250873b52b2fb5453fb7d80df912db929add6e4b7efc066363"
+ },
+ {
+ "name": "ski",
+ "unicode": "1F3BF",
+ "digest": "7ee81a2e2f7ff4e32dbf3d64b034e7542ec0c86d32e25eb125052e674943d75f"
+ },
+ {
+ "name": "skier",
+ "unicode": "26F7",
+ "digest": "49df9a4206ae0c7c2dbfc8a8b13fd3e14e6f7e750bd5a8581ab6a1626d4c165e"
+ },
+ {
+ "name": "skull",
+ "unicode": "1F480",
+ "digest": "dfd169764b192ac7c6e5101277dd9f1e010e86bdd32ad37e00ed4499fc0a5dd6"
+ },
+ {
+ "name": "skull_crossbones",
+ "unicode": "2620",
+ "digest": "e2acf0f36b6a6800c1829a1c6551b5d0eb6dcdef4b7f02070cf69570aeab608c"
+ },
+ {
+ "name": "sleeping",
+ "unicode": "1F634",
+ "digest": "4ead95079b1a542eedd0e5a0e93fddb318a002bdaffaa2fe5d8d7f20bf8143ed"
+ },
+ {
+ "name": "sleeping_accommodation",
+ "unicode": "1F6CC",
+ "digest": "10ee8cd925a75d7977b7cf004e08b5a8147b509ee4281e879a8b57c4a7c2cb04"
+ },
+ {
+ "name": "sleepy",
+ "unicode": "1F62A",
+ "digest": "dea3b246bb8af1b28e200358e3d5d59c8bba1813f35a7f4a57ec568ef43591db"
+ },
+ {
+ "name": "slight_frown",
+ "unicode": "1F641",
+ "digest": "3ae82b38b58ffa50eddebd87153428d880ca181f4f4178a9ca3bd813ea15ccbc"
+ },
+ {
+ "name": "slight_smile",
+ "unicode": "1F642",
+ "digest": "5eee09f634a4e2031927d008a6530a258a00e611ead0c386dd5b7ebb5e75a306"
+ },
+ {
+ "name": "slot_machine",
+ "unicode": "1F3B0",
+ "digest": "9d516b389299431b608c89d3f02ac68d28cb8df2a780f2048923bbcfbb49f416"
+ },
+ {
+ "name": "small_blue_diamond",
+ "unicode": "1F539",
+ "digest": "97389e82755dc43015089dee635072357ec347f0117b2d3e9b006c46514948ee"
+ },
+ {
+ "name": "small_orange_diamond",
+ "unicode": "1F538",
+ "digest": "67442d3b707501b7768f606115688373d13617ecf0b3b03ace0f1a6d38f66ddf"
+ },
+ {
+ "name": "small_red_triangle",
+ "unicode": "1F53A",
+ "digest": "e0a556a3dd5bbf0290ed7c00eb6f6307dc2ea98d1fb3111fd85a7f46242a3638"
+ },
+ {
+ "name": "small_red_triangle_down",
+ "unicode": "1F53B",
+ "digest": "7a11dcb8a517df220493d471759e4f4bca0db3769e2d942bbf596a88a3e57f72"
+ },
+ {
+ "name": "smile",
+ "unicode": "1F604",
+ "digest": "46a7c3545b0038dfce6825d97544f6665f28512ad05c404d668e32ac599c7ecb"
+ },
+ {
+ "name": "smile_cat",
+ "unicode": "1F638",
+ "digest": "c1db961f0fa261532b842816aca7ea7f6d8b461c7e930a1a1c91f96efd9db515"
+ },
+ {
+ "name": "smiley",
+ "unicode": "1F603",
+ "digest": "deeaaee64ebdd9fc0bcb719db75c3f7e0c33ddbcc97f6cd51f9f84377a4368ce"
+ },
+ {
+ "name": "smiley_cat",
+ "unicode": "1F63A",
+ "digest": "85ad852cb3881c4b754af172fdfc6231af42578033ea9f2981ceae944c41e72f"
+ },
+ {
+ "name": "smiling_imp",
+ "unicode": "1F608",
+ "digest": "e777bdf186d89921df106d23bf002967b69afffd7e981b3cbb19f89630a06e87"
+ },
+ {
+ "name": "smirk",
+ "unicode": "1F60F",
+ "digest": "2e7fddd8bed33ef4b7d8c13320302b87a28203e576ef87bd43716952cf0b5ace"
+ },
+ {
+ "name": "smirk_cat",
+ "unicode": "1F63C",
+ "digest": "9ca0721f4c18592b4b809ade8f716b95fa30cd31dd87d1e41db29a319becd705"
+ },
+ {
+ "name": "smoking",
+ "unicode": "1F6AC",
+ "digest": "3d14b3f0c57eb7a6a31ff371b0a454986533b79dbbeac78a76e4063478911b8d"
+ },
+ {
+ "name": "snail",
+ "unicode": "1F40C",
+ "digest": "57d946c7ec84dfad71bc4f7a042927ec5712aef50c66d21af892b6c8a7faf5e1"
+ },
+ {
+ "name": "snake",
+ "unicode": "1F40D",
+ "digest": "d084da540162288721364992f3b8059cbf2efd9f5b48f49a196ddbe23a073870"
+ },
+ {
+ "name": "snowboarder",
+ "unicode": "1F3C2",
+ "digest": "de9e1767526de606f4908743af94cc17e89fdb0a2a44167d3d021ef09d033ab9"
+ },
+ {
+ "name": "snowflake",
+ "unicode": "2744",
+ "digest": "e476863ccd7d7b549c6191fb25c121c6a467b4baef4683b7dc3e0a793c2e5d76"
+ },
+ {
+ "name": "snowman",
+ "unicode": "26C4",
+ "digest": "792946b8446f2243d11b89d07c73a774be3abd36573f3918640b1ba8714270b5"
+ },
+ {
+ "name": "snowman2",
+ "unicode": "2603",
+ "digest": "571acabaa4d55782c4529b762423a7e34cb1fb6bb7852cbd013e2e846d8311d1"
+ },
+ {
+ "name": "sob",
+ "unicode": "1F62D",
+ "digest": "562f02ab584bcbcf9ba73cf7fa7d7129965266abd28db2c73913b8c42f2f5aca"
+ },
+ {
+ "name": "soccer",
+ "unicode": "26BD",
+ "digest": "5fd0d534659b63dc862c65a80561b255bece0b76708fe8ecbae8e01b08d8cad0"
+ },
+ {
+ "name": "soon",
+ "unicode": "1F51C",
+ "digest": "d2a1ab16a4056d80c827ea23f9332bb73235fc841b857cbf545062ff8aeed81d"
+ },
+ {
+ "name": "sos",
+ "unicode": "1F198",
+ "digest": "fadfe8337e133a6f05d205d0807f288e5c230db04cb09f3547ce0cb73cfcf48a"
+ },
+ {
+ "name": "sound",
+ "unicode": "1F509",
+ "digest": "c0074b338fd461f1f9d1143b7f9b3781ddb3fd501ea79b2410630433a8e87b83"
+ },
+ {
+ "name": "space_invader",
+ "unicode": "1F47E",
+ "digest": "d264390004bd28d664dfda0069104be6db32ce477e23a95ac595bac2e29fd4e7"
+ },
+ {
+ "name": "spades",
+ "unicode": "2660",
+ "digest": "d1ad99a4fc20dfea881a9062a9f2109e483dbb5dea3b29e9653cb27ec57b4800"
+ },
+ {
+ "name": "spaghetti",
+ "unicode": "1F35D",
+ "digest": "ac63f9ad143e236ce6068098e5330a333ade9cddfb3dd6b1457ea47ce9dcf7e9"
+ },
+ {
+ "name": "sparkle",
+ "unicode": "2747",
+ "digest": "95b8f4f1bb6080cd1d7bd333c4724dbba43ed196dce72a2bbaab46c4a1bc0e48"
+ },
+ {
+ "name": "sparkler",
+ "unicode": "1F387",
+ "digest": "3a296e4d0081ad1a566e111d218e352e1439bba9fd04e8a1eb9a8e36bd438cb7"
+ },
+ {
+ "name": "sparkles",
+ "unicode": "2728",
+ "digest": "5ab280ea10c30e0e0b5a26ef52b8f47ad44a983330f7ef62ac0c0888752bbdb6"
+ },
+ {
+ "name": "sparkling_heart",
+ "unicode": "1F496",
+ "digest": "f145dab6b597c07e5a851176fabaf56dd857209645483d1acc1490d12c969113"
+ },
+ {
+ "name": "speak_no_evil",
+ "unicode": "1F64A",
+ "digest": "6eae2d066d39c4ba81e58a8327ed875c68bc9b1297c18dc0f5243e477a81040f"
+ },
+ {
+ "name": "speaker",
+ "unicode": "1F508",
+ "digest": "ea59c5a9d994808ff7937c300303e644b5f1ad41097e82f9e73ea6e1c718936c"
+ },
+ {
+ "name": "speaking_head",
+ "unicode": "1F5E3",
+ "digest": "d92cfe1200887300b2f05f9576448a2f2a79d0accd51f323a65ce3db0aa5639b"
+ },
+ {
+ "name": "speech_balloon",
+ "unicode": "1F4AC",
+ "digest": "5dccfda46fc984583bc9eaece66e7e884f2a9eb12a69dbd3493035e3c862edd0"
+ },
+ {
+ "name": "speech_left",
+ "unicode": "1F5E8",
+ "digest": "478b0b07460a9f54b7d0050f886da59fde5e428daa11e899fc31477fda1707ed"
+ },
+ {
+ "name": "speech_right",
+ "unicode": "1F5E9",
+ "digest": "8439b13779163c15e678a78b08ebeeb7d131632df21d2a7868de7fed38ca9d8a"
+ },
+ {
+ "name": "speech_three",
+ "unicode": "1F5EB",
+ "digest": "55a934f3659b6e75fdce0d0c4e2ea56dd34a43892c85a6666bd1882a0bfb92a9"
+ },
+ {
+ "name": "speech_two",
+ "unicode": "1F5EA",
+ "digest": "0563ef0591da243673cf877462acc5d8e1d980a56e81668ac627de74d0c33983"
+ },
+ {
+ "name": "speedboat",
+ "unicode": "1F6A4",
+ "digest": "553a288ab8eeb3dee7b9d1c92eba38016caef7658beaa828136ba1d6ba8ed08a"
+ },
+ {
+ "name": "spider",
+ "unicode": "1F577",
+ "digest": "519f7243b5574102ce3f8953e5480812830a1feb32ae51e8573724c864338481"
+ },
+ {
+ "name": "spider_web",
+ "unicode": "1F578",
+ "digest": "42959fae08a2162d6ee8c8706f823c5932f3801bc90da30d2ca9a48c3ff25572"
+ },
+ {
+ "name": "spy",
+ "unicode": "1F575",
+ "digest": "eaa570a36d83119d0a596228e74affe84d7355714ff6901d88a89410d26dec2a"
+ },
+ {
+ "name": "spy_tone1",
+ "unicode": "1F575-1F3FB",
+ "digest": "abdc066d4cad6a17047faf7806c45feb43ae1e2056cf500536f08f4173dbfa94"
+ },
+ {
+ "name": "spy_tone2",
+ "unicode": "1F575-1F3FC",
+ "digest": "72a3313ef12364105e764cc3deabd47eb6bd086f261c435682ae1cd29dc8230b"
+ },
+ {
+ "name": "spy_tone3",
+ "unicode": "1F575-1F3FD",
+ "digest": "2a1108d3d2e778f88aa5b3ae36705c877b84d0bf6b421409582ba748aeb2aee7"
+ },
+ {
+ "name": "spy_tone4",
+ "unicode": "1F575-1F3FE",
+ "digest": "1d4fe62912384bc0d687bcf4565752caf0ed6146c903a156d1c6ba6ea239b154"
+ },
+ {
+ "name": "spy_tone5",
+ "unicode": "1F575-1F3FF",
+ "digest": "69c1baac73783edb9e2d0c951f922dc7dddac34d0a9c818fee8d1021bc17db0d"
+ },
+ {
+ "name": "stadium",
+ "unicode": "1F3DF",
+ "digest": "4356db5d2cdef8c40830638debaf1f50831130c12ae8d8dc3d9a6bd28fdaa1f7"
+ },
+ {
+ "name": "star",
+ "unicode": "2B50",
+ "digest": "13240b8fada84e7555892996e9f9652503bf9b9a002056c2bae428d543abe2da"
+ },
+ {
+ "name": "star2",
+ "unicode": "1F31F",
+ "digest": "9b56c7548f6a222499d4e848576ea25eab837db72b207ebf8a62a451b35f758f"
+ },
+ {
+ "name": "star_and_crescent",
+ "unicode": "262A",
+ "digest": "10b8a0771e415aa6610fa62185137aa1836c2bb3e82f1a3f601470e94f784923"
+ },
+ {
+ "name": "star_of_david",
+ "unicode": "2721",
+ "digest": "5bc4d1038b8316281e01a9c575ded7ede0fc24c7593db5b5d36ca2e188aa5614"
+ },
+ {
+ "name": "stars",
+ "unicode": "1F320",
+ "digest": "23605eafc949feead3eca145a7ff5ee3b211a8bfd95621bd35dd05df532b97c6"
+ },
+ {
+ "name": "station",
+ "unicode": "1F689",
+ "digest": "c346f12fff64161041af8492550c3541a6304e53f30288224ddd0c6fe08c4d6b"
+ },
+ {
+ "name": "statue_of_liberty",
+ "unicode": "1F5FD",
+ "digest": "56fa27ab059a9fd1f53aec47d9108277a3bf04a73186f36297cd1207c832ee31"
+ },
+ {
+ "name": "steam_locomotive",
+ "unicode": "1F682",
+ "digest": "d0ec2eb3d761ab6157e17eab1b8b4dec3a69f9becc4251592cbb67d71825e661"
+ },
+ {
+ "name": "stereo",
+ "unicode": "1F4FE",
+ "digest": "1ce1f9a83867514b8351ad4fd80c46bba04ad67dfb9874e63d7296e1a21161a5"
+ },
+ {
+ "name": "stew",
+ "unicode": "1F372",
+ "digest": "12e6e4bf48a7296700e07a053d831dd67b70c308ca9522ca96e933a4d1ef6c5e"
+ },
+ {
+ "name": "stock_chart",
+ "unicode": "1F5E0",
+ "digest": "4a0fbf54d19b0b5626f91c932a24e6ac12a65b4fc276d852ff4356c8c579d28a"
+ },
+ {
+ "name": "stop_button",
+ "unicode": "23F9",
+ "digest": "57310962c7738a7da4f2a62cbd5e0b26d7aec357978267a0d8ca8e6cbd7ffb02"
+ },
+ {
+ "name": "stopwatch",
+ "unicode": "23F1",
+ "digest": "c8e69c24f9da98dcb41c9c6355922d08a702f12a35667fbc5beb3f659430333d"
+ },
+ {
+ "name": "straight_ruler",
+ "unicode": "1F4CF",
+ "digest": "55ff7182a3696461df52e3000708083f803bc8bf0f3c25dacb34175cc104b51d"
+ },
+ {
+ "name": "strawberry",
+ "unicode": "1F353",
+ "digest": "fd501e1fefb70242ac7c4dc30ad3d8c3ae200b263a832daedaa984906114afaf"
+ },
+ {
+ "name": "stuck_out_tongue",
+ "unicode": "1F61B",
+ "digest": "1b49956cec511ee382177d95da77c8b6a9214a02c86bf7c6c6fd6cc9df3e9331"
+ },
+ {
+ "name": "stuck_out_tongue_closed_eyes",
+ "unicode": "1F61D",
+ "digest": "60a4d5d92550c6ad4db901d42c9f6434fe94fa3ddb353b6019a93d374d9485e9"
+ },
+ {
+ "name": "stuck_out_tongue_winking_eye",
+ "unicode": "1F61C",
+ "digest": "d9c15ad1c4782a0391a79aeda2745127527385b0b5fc01c8d96c3f3b637a74ae"
+ },
+ {
+ "name": "sun_with_face",
+ "unicode": "1F31E",
+ "digest": "56b14e92f68f8701fdc42763e1f4695ed352845f22bd5d412f827e5cf98dd83b"
+ },
+ {
+ "name": "sunflower",
+ "unicode": "1F33B",
+ "digest": "817dea222a75bb6492c32b4b144d07f48295d7dd113e21760f90b18277612ebb"
+ },
+ {
+ "name": "sunglasses",
+ "unicode": "1F60E",
+ "digest": "16003cc5256397389889f52e0a5e14daea8d8c72f2ea660b8174529868cba9cd"
+ },
+ {
+ "name": "sunny",
+ "unicode": "2600",
+ "digest": "f68a774b7d574fc711111e17368b57c40d973d263c7e857544a09051d4592ab9"
+ },
+ {
+ "name": "sunrise",
+ "unicode": "1F305",
+ "digest": "ce06a9321bc04605538a59f9fca8536d6209d7ded03120e5d2a0be955bb17ddf"
+ },
+ {
+ "name": "sunrise_over_mountains",
+ "unicode": "1F304",
+ "digest": "286244ac2bec8c5c41cf8c7c439702fa525c57fab623f7f9bd7687db0adf75b2"
+ },
+ {
+ "name": "surfer",
+ "unicode": "1F3C4",
+ "digest": "d17c7ea185ca5ef5a2950ef126ee14103bf7769acb419a20d08cc023f619e459"
+ },
+ {
+ "name": "surfer_tone1",
+ "unicode": "1F3C4-1F3FB",
+ "digest": "af66f2f26071b3ba8d7c795139055a58a857212f8cb1f51a507242ad7d2c49c7"
+ },
+ {
+ "name": "surfer_tone2",
+ "unicode": "1F3C4-1F3FC",
+ "digest": "7a34e8b1fdad0a89bbb10333d241583ef018517fdd90f171ad7121de53776a3f"
+ },
+ {
+ "name": "surfer_tone3",
+ "unicode": "1F3C4-1F3FD",
+ "digest": "b2f4cbd59a0aa93c7ee2bbb14ce55c8306dc25884377982a5f132ce6c074fa1d"
+ },
+ {
+ "name": "surfer_tone4",
+ "unicode": "1F3C4-1F3FE",
+ "digest": "b16a02cfcc3606524cca9408e69c654fb83a162eaec8faae8dfd8ec67fe391c5"
+ },
+ {
+ "name": "surfer_tone5",
+ "unicode": "1F3C4-1F3FF",
+ "digest": "b9a156e1aa57544b703db4e4a7773e244a3139e82c2c808c2e5a804fb524f512"
+ },
+ {
+ "name": "sushi",
+ "unicode": "1F363",
+ "digest": "d2709b51ee92997c7fafa1b1517259cb896819c8dc9ba98ae26e1d44ec810d4f"
+ },
+ {
+ "name": "suspension_railway",
+ "unicode": "1F69F",
+ "digest": "48903e103ef00a068b0100b28319b1e41c6a4485cb564f0ca59422ec9d3b259c"
+ },
+ {
+ "name": "sweat",
+ "unicode": "1F613",
+ "digest": "8d684fa882bcbf07f4e91ea02a48cd61f22e7aa206162b8352c26fc19361ed4e"
+ },
+ {
+ "name": "sweat_drops",
+ "unicode": "1F4A6",
+ "digest": "fca48e255dff08dab97ef98b75c67f7504a13be8b90afac88b69a7b7e887e445"
+ },
+ {
+ "name": "sweat_smile",
+ "unicode": "1F605",
+ "digest": "0c8156554eec2396b5fee908da46484945db980d2ebc6dee57b4069a86826182"
+ },
+ {
+ "name": "sweet_potato",
+ "unicode": "1F360",
+ "digest": "3ce74ea9bc14906a3d29a9592c0657aee8f7961d406992752f7580b16ca6bdd0"
+ },
+ {
+ "name": "swimmer",
+ "unicode": "1F3CA",
+ "digest": "05f3aa8544e3b15837bb06ae47344633b3e60d64c572dc6638c4cee19d6e5506"
+ },
+ {
+ "name": "swimmer_tone1",
+ "unicode": "1F3CA-1F3FB",
+ "digest": "85a266a9131f6a1b37e758305ca43ffb46e3e07b0a465c5faefbdb5e5adeb7a4"
+ },
+ {
+ "name": "swimmer_tone2",
+ "unicode": "1F3CA-1F3FC",
+ "digest": "f2afdc4d05a2694e663a420d5ad82bd48c92aedc4137d0fd3725bf08c41bd12a"
+ },
+ {
+ "name": "swimmer_tone3",
+ "unicode": "1F3CA-1F3FD",
+ "digest": "b87ecc38fb9e8eeeef8b120164d758d3f6a68a407053b03261354fd7f90f43b6"
+ },
+ {
+ "name": "swimmer_tone4",
+ "unicode": "1F3CA-1F3FE",
+ "digest": "a08629cf3484953b851b357c6a04891fb97ac15e70c376bbb82af47479835e1c"
+ },
+ {
+ "name": "swimmer_tone5",
+ "unicode": "1F3CA-1F3FF",
+ "digest": "21d83f66b2ef3e348f9e14ec108b9a90262d9934039ebd573471d2bdcde68974"
+ },
+ {
+ "name": "symbols",
+ "unicode": "1F523",
+ "digest": "f33c3ce58374e23b8957c759016fdb5c56ef7fe812bd4e693ae8ff7574cf6bbf"
+ },
+ {
+ "name": "synagogue",
+ "unicode": "1F54D",
+ "digest": "b13402c3c5793ebf924335a87a9f69befb7a6c152fc2a288261b2c2d49842eb6"
+ },
+ {
+ "name": "syringe",
+ "unicode": "1F489",
+ "digest": "39e5e7530255ccf2ff35ec5c653568c8645a4711170c573117f796ea3438c44a"
+ },
+ {
+ "name": "taco",
+ "unicode": "1F32E",
+ "digest": "6b004ce7129e00abcc10278bba1b9c3d5ac71888b99bf353f9878d8e494e3e0d"
+ },
+ {
+ "name": "tada",
+ "unicode": "1F389",
+ "digest": "956a180a1f18e3a1252761e5b3713324f63975ee1fe32168b59b60aa4dd8b72b"
+ },
+ {
+ "name": "tanabata_tree",
+ "unicode": "1F38B",
+ "digest": "d074457ba347687bfc8397ec62edee6325c411356216e7d43acd3f60628a0bb8"
+ },
+ {
+ "name": "tangerine",
+ "unicode": "1F34A",
+ "digest": "1b46bb690458914220cba18c43d7ae0f6914adfee6dba7cf2bb58ed4e1854ad8"
+ },
+ {
+ "name": "taurus",
+ "unicode": "2649",
+ "digest": "ea87fb3baa32605107d63b60847e4873ad9e21b7e7b652e3721cde777168670d"
+ },
+ {
+ "name": "taxi",
+ "unicode": "1F695",
+ "digest": "f44249c643a96d924e1eb35f67a133f3ca61128e610a880afaa09a73c7bcaf9d"
+ },
+ {
+ "name": "tea",
+ "unicode": "1F375",
+ "digest": "56ab8c291de8320c5b339e1cfbe972696e4ea31c592cefa240eda9a3abdf4fa3"
+ },
+ {
+ "name": "telephone",
+ "unicode": "260E",
+ "digest": "609104588e00039199a2fef3190ee6a7be5fca7cb09b36ffe5a7d800aac69d8d"
+ },
+ {
+ "name": "telephone_black",
+ "unicode": "1F57F",
+ "digest": "c3a42a653a91d90c6b668f678419d5438f2e546050914b841623e57107e805db"
+ },
+ {
+ "name": "telephone_receiver",
+ "unicode": "1F4DE",
+ "digest": "e3bf6034de6cf2160893ba4990eba198185a6a3f9cd5767a63b048e41c297640"
+ },
+ {
+ "name": "telephone_white",
+ "unicode": "1F57E",
+ "digest": "62a7e0e50c53e9f85eba51a92882e6064be05997910d3f7700e1e957dbaf0581"
+ },
+ {
+ "name": "telescope",
+ "unicode": "1F52D",
+ "digest": "abe0aca5f2c78105b0e9e4c8ee7a40adcd9bb013e7c49d568076459bade73556"
+ },
+ {
+ "name": "ten",
+ "unicode": "1F51F",
+ "digest": "7593aa7ffe7192a2e35c6ccec76522f6243777783c9152c7c03419835ea58c03"
+ },
+ {
+ "name": "tennis",
+ "unicode": "1F3BE",
+ "digest": "0a5fad3f7f35da0f37761e2279c148dbe154fa14c0e2a0749209b8b2b213a388"
+ },
+ {
+ "name": "tent",
+ "unicode": "26FA",
+ "digest": "7ddf437d8d186e4e3c3e818d137518d590fa06098813c7fe20e1f2a9704feab2"
+ },
+ {
+ "name": "thermometer",
+ "unicode": "1F321",
+ "digest": "597d1714442698a22187fee4d57a2580322f7206c7d51e4519023824598ec08f"
+ },
+ {
+ "name": "thermometer_face",
+ "unicode": "1F912",
+ "digest": "f19c489d89dd2d39770a6c8725a20f3e98f9e5216774af60c0665fd6a03a7687"
+ },
+ {
+ "name": "thinking",
+ "unicode": "1F914",
+ "digest": "f64a9a18dca4c502b46f933838753a818b604a9d0268aa32eda26cbd31abc58c"
+ },
+ {
+ "name": "thought_balloon",
+ "unicode": "1F4AD",
+ "digest": "76c8513191641f0a79e878ccc0d83c4576984609810633f596db2f64cc684b7d"
+ },
+ {
+ "name": "thought_left",
+ "unicode": "1F5EC",
+ "digest": "4fd591bf4318df73d1b17f434a449d8e95f49cca53a3d8f4d1ca983f3809ef46"
+ },
+ {
+ "name": "thought_right",
+ "unicode": "1F5ED",
+ "digest": "0e8c0ce26e2d0e30894f5394b0736456e8268f775e0e7eda4c7dc3c2ff9231ae"
+ },
+ {
+ "name": "three",
+ "unicode": "0033-20E3",
+ "digest": "ca0147a8f67cea3bc2516fa8deef4325188359559786c94ff0b27f90eef04b88"
+ },
+ {
+ "name": "thumbs_down_reverse",
+ "unicode": "1F593",
+ "digest": "a8b561e389bc4e4b07fba70994f6445e5ddc6afe68922fcb6e9e7282d19ad958"
+ },
+ {
+ "name": "thumbs_up_reverse",
+ "unicode": "1F592",
+ "digest": "b6e52715c5ce590bfd08f6e05058ec3765ea2da341b11f9825d100608b173837"
+ },
+ {
+ "name": "thumbsdown",
+ "unicode": "1F44E",
+ "digest": "a98f742c9773e0d95c0de5e1c10d1ab373fa761378a205f27d095e85debe69a3"
+ },
+ {
+ "name": "thumbsdown_tone1",
+ "unicode": "1F44E-1F3FB",
+ "digest": "5d0a7c63d52eafe6267c552168c5557a66622009d565c3cf7b5378c1f6e84bce"
+ },
+ {
+ "name": "thumbsdown_tone2",
+ "unicode": "1F44E-1F3FC",
+ "digest": "ca5c15dc516660b2989a1c717bf3745fdfb6964c7acf3b938285ff6c7caf2ca2"
+ },
+ {
+ "name": "thumbsdown_tone3",
+ "unicode": "1F44E-1F3FD",
+ "digest": "05740e3568795270674dac9134198bf75b1b778c11daa71649c88c231859ec16"
+ },
+ {
+ "name": "thumbsdown_tone4",
+ "unicode": "1F44E-1F3FE",
+ "digest": "5ee93bcc2f515806462a7b303064beade2b22a3f43a8162e39fd65d15d772e27"
+ },
+ {
+ "name": "thumbsdown_tone5",
+ "unicode": "1F44E-1F3FF",
+ "digest": "5c9ef8d53cf6f755668ab6dabfbfcdfd4b95fd59db3b3dd60290efefe9c33994"
+ },
+ {
+ "name": "thumbsup",
+ "unicode": "1F44D",
+ "digest": "28b31df963773ba42a1a089f43cd89d0ce1ab0981e5410f41242e9a125fc1aee"
+ },
+ {
+ "name": "thumbsup_tone1",
+ "unicode": "1F44D-1F3FB",
+ "digest": "f6365942738d2128b6959d6672b3d295757dc8240703cb84a2b014ad78d67de3"
+ },
+ {
+ "name": "thumbsup_tone2",
+ "unicode": "1F44D-1F3FC",
+ "digest": "771d30146e4dc947a69057b05d32c765c8457ab02b5342889c5489acf27ef356"
+ },
+ {
+ "name": "thumbsup_tone3",
+ "unicode": "1F44D-1F3FD",
+ "digest": "0bb7bbfb654c6139260e1786e7ffa5a33f31e19410c1d4d15737fdf5dd4c721d"
+ },
+ {
+ "name": "thumbsup_tone4",
+ "unicode": "1F44D-1F3FE",
+ "digest": "df0927c5342f0075fbf4ea83b724e6f70c0466c54769c9ce4a5c2deb602b28aa"
+ },
+ {
+ "name": "thumbsup_tone5",
+ "unicode": "1F44D-1F3FF",
+ "digest": "0683ae08c50aaf186c6406680a60617679c7b4bccd0817f24b15911dbb06866f"
+ },
+ {
+ "name": "thunder_cloud_rain",
+ "unicode": "26C8",
+ "digest": "dd836f06b41a10d6ed9bcbdae291d2886847ff66dc3ede2427382e469f60674c"
+ },
+ {
+ "name": "ticket",
+ "unicode": "1F3AB",
+ "digest": "a7654a5529535120da3c377e72cd1f7997bdc2dabf1d44b584f7df7852b158f9"
+ },
+ {
+ "name": "tickets",
+ "unicode": "1F39F",
+ "digest": "ccafcc9583a84e847ff1eaa3d53187c5ab150a7d27c6a19363e59b9bc046b567"
+ },
+ {
+ "name": "tiger",
+ "unicode": "1F42F",
+ "digest": "9ebe3117f5f1b589ff8164f8d87dcc275923e0db87121d2cee0fdb9b56dfc4ac"
+ },
+ {
+ "name": "tiger2",
+ "unicode": "1F405",
+ "digest": "212c95dc60d52420a6320917fe3fdd0683b4edc1a2a2c4a1c60920d1f90f4bc3"
+ },
+ {
+ "name": "timer",
+ "unicode": "23F2",
+ "digest": "c48199312ed42ff53a33bb2791db19e2e2521223cd49d8f758ea95b9b379c5ff"
+ },
+ {
+ "name": "tired_face",
+ "unicode": "1F62B",
+ "digest": "ad687a956388ec53ca1e301a0abe2f1e2cfb9f73cd543dd61a21c7335a42e332"
+ },
+ {
+ "name": "tm",
+ "unicode": "2122",
+ "digest": "1156c8b0af40b336bbb6534b3302ac63eab009c4cd0476adcf1fc4669f04b647"
+ },
+ {
+ "name": "toilet",
+ "unicode": "1F6BD",
+ "digest": "a4a24529c21e00e0861f4160c771f0e90aae8f6aee7550ad30d3dbb3fabbd4be"
+ },
+ {
+ "name": "tokyo_tower",
+ "unicode": "1F5FC",
+ "digest": "6324f154f5f5c722044129e5bca03484aca1439911585e42c1c181ffa30b480c"
+ },
+ {
+ "name": "tomato",
+ "unicode": "1F345",
+ "digest": "41bb6de095b27815eacb74a70aea8f7d4fe1ff947182b112001dd47ae7e45fbb"
+ },
+ {
+ "name": "tone1",
+ "unicode": "1F3FB",
+ "digest": "5c62003a098b774c068be45d658db3c0dd38483c0871f7c8ae293bc1222c4f0c"
+ },
+ {
+ "name": "tone2",
+ "unicode": "1F3FC",
+ "digest": "3c636ecbc4e58c7a360f2338daaf44e7da598fd07e0ba1514bb5c0f83fc8819f"
+ },
+ {
+ "name": "tone3",
+ "unicode": "1F3FD",
+ "digest": "398a1e5441b64c9c2d033bbc01d7a8d90b4db30ea9f30e28f0a9120c72a48df8"
+ },
+ {
+ "name": "tone4",
+ "unicode": "1F3FE",
+ "digest": "ff4a12195aeb7494c785b81266efad8cd60c8022c407a0fc032a02e8b83216b3"
+ },
+ {
+ "name": "tone5",
+ "unicode": "1F3FF",
+ "digest": "9e9f0125b5d57011b7456c84719e6be6cf71d06c1b198081d0937c0979164a81"
+ },
+ {
+ "name": "tongue",
+ "unicode": "1F445",
+ "digest": "bf9dd7c65a8dc5d77eb013658a0a12a13f7b224a784e65e203d9584bb6b41427"
+ },
+ {
+ "name": "tools",
+ "unicode": "1F6E0",
+ "digest": "9b0a36dfdb475621d326359662b22cbdb80563c4f476aa5e7d7c00cdba605bd9"
+ },
+ {
+ "name": "top",
+ "unicode": "1F51D",
+ "digest": "d645030099aeb433307569e8e1c4342c1c411a8fefe50fdca7a3207a1a0db671"
+ },
+ {
+ "name": "tophat",
+ "unicode": "1F3A9",
+ "digest": "1082fb2ee2e98fe65d21081b74ca59b07adef85043e2d36f25cac69db2d31fd3"
+ },
+ {
+ "name": "track_next",
+ "unicode": "23ED",
+ "digest": "d5415ed140933f345fea8023a3d8fca30dcfcf7d19d9dc9771fa2cae9df62a3b"
+ },
+ {
+ "name": "track_previous",
+ "unicode": "23EE",
+ "digest": "97ff4a59a236e5cf506fa3577b20715b3b0197e0f343a50615b36185d5b835f1"
+ },
+ {
+ "name": "trackball",
+ "unicode": "1F5B2",
+ "digest": "8332503454ce42059d720c285fe2b15eb0562a0a4b234dccb0f3159bb30a91aa"
+ },
+ {
+ "name": "tractor",
+ "unicode": "1F69C",
+ "digest": "a41d304c41a85d966f6a7c301735fdbe2ae41f4471dd7dcd72023046ca2546d0"
+ },
+ {
+ "name": "traffic_light",
+ "unicode": "1F6A5",
+ "digest": "005f68d028fec8d9ae389cc2b23e1343a82c028eb32820d5e56f5c84eba315d1"
+ },
+ {
+ "name": "train",
+ "unicode": "1F68B",
+ "digest": "bf32893b7b9ecd248e8afe840624061746ac6ceb741e3e861ebfa46014f4bed4"
+ },
+ {
+ "name": "train2",
+ "unicode": "1F686",
+ "digest": "08a9732453a0b4f68dd2d3d3879f04ee538f65897913b5a5157c0585132a374a"
+ },
+ {
+ "name": "train_diesel",
+ "unicode": "1F6F2",
+ "digest": "621bb967cd93fa9f8fd4b155965cc7572d3f91f88d94938ba10c8626718b623c"
+ },
+ {
+ "name": "tram",
+ "unicode": "1F68A",
+ "digest": "5a86d31f7ab677d967fecd75babc900b5169766d0228961912314c4c4d1d64ee"
+ },
+ {
+ "name": "triangle_round",
+ "unicode": "1F6C6",
+ "digest": "e24bb39ecfaaa746b03dc8418697d09ef327d5b077db39014f39d5fb87e23bd5"
+ },
+ {
+ "name": "triangular_flag_on_post",
+ "unicode": "1F6A9",
+ "digest": "d824c973d84cd62c845d64e546de87b094fda8f9972b6a33acd75e1a5ac19f75"
+ },
+ {
+ "name": "triangular_ruler",
+ "unicode": "1F4D0",
+ "digest": "5576802d8bcb8836f473d9c7641ff666250c23c8476c676b253e577695025959"
+ },
+ {
+ "name": "trident",
+ "unicode": "1F531",
+ "digest": "70c1e8254da5b0e4552673b487503a20feeb249484d4596836b75de70220be82"
+ },
+ {
+ "name": "triumph",
+ "unicode": "1F624",
+ "digest": "b09262121b0d3d9d017ded22d0fbb1acaa6ee8c9d38e9ac34292b390d97408fe"
+ },
+ {
+ "name": "trolleybus",
+ "unicode": "1F68E",
+ "digest": "5af943836cc30c3b79160c70b6488c984fa63c104dce08c436597a93d30ff6f4"
+ },
+ {
+ "name": "trophy",
+ "unicode": "1F3C6",
+ "digest": "c249938815042716db2b39cdece6715fabf9e56ed583270c451925e6c91f9191"
+ },
+ {
+ "name": "tropical_drink",
+ "unicode": "1F379",
+ "digest": "352d903e813a27d2a74803322539b50a50aec0ca2ed7ab4a92ec480b1c226cb6"
+ },
+ {
+ "name": "tropical_fish",
+ "unicode": "1F420",
+ "digest": "13a104ca9c326238ab8d85b60759629b4efaa836946fbe58d78d779443475f7b"
+ },
+ {
+ "name": "truck",
+ "unicode": "1F69A",
+ "digest": "13d381d6b43b42350a1e24c02296904b8fdc38c1bf0939fc7037850127e91f21"
+ },
+ {
+ "name": "trumpet",
+ "unicode": "1F3BA",
+ "digest": "df7fb48920ac0919ee2d7b30102016479f747a5d4dd25b3e18d9f17121d232d1"
+ },
+ {
+ "name": "tulip",
+ "unicode": "1F337",
+ "digest": "519a84336464b5dc8db57eecef3e5b8ed82ccfdaa0ed0fa9ef7bcf0e8acea1f8"
+ },
+ {
+ "name": "turkey",
+ "unicode": "1F983",
+ "digest": "e87bff52ad3e301dc62f6832b8a6fcaf99db260a96263e4203a55ce3abda8cf8"
+ },
+ {
+ "name": "turned_ok_hand",
+ "unicode": "1F58F",
+ "digest": "8a6c5b7d4c737866e7e32c6d9f7f447a48a0ac57a8909d43f87367d4a9b59246"
+ },
+ {
+ "name": "turtle",
+ "unicode": "1F422",
+ "digest": "388b3e75b931638a09f65b842d26e2cc87b200ba782dec871f84cddd71aaeaf3"
+ },
+ {
+ "name": "tv",
+ "unicode": "1F4FA",
+ "digest": "dba03be6482d6291599c7393b0f749c0de5c873d45c96a20ccc53b3e104a6a24"
+ },
+ {
+ "name": "twisted_rightwards_arrows",
+ "unicode": "1F500",
+ "digest": "5fcad0247576e10e683f353008749975e9371a4f66c0901a73c3a0c7803c63c7"
+ },
+ {
+ "name": "two",
+ "unicode": "0032-20E3",
+ "digest": "20ad722532a5073fff8aef0a5e890421da0ae97f0723a8a2cc503c13d24ba597"
+ },
+ {
+ "name": "two_hearts",
+ "unicode": "1F495",
+ "digest": "160cb11e3ed2ae1b20957d445c6c4b4bd604d067294818dfeeefba4562425eb9"
+ },
+ {
+ "name": "two_men_holding_hands",
+ "unicode": "1F46C",
+ "digest": "923734704e544f7484fdb424bfe26f51ee07754db712cd151f8fbe955023a1ab"
+ },
+ {
+ "name": "two_women_holding_hands",
+ "unicode": "1F46D",
+ "digest": "58a40e7819cab3589ac81bb4fdc485b7196ee355544b54c6b00169028c260130"
+ },
+ {
+ "name": "u5272",
+ "unicode": "1F239",
+ "digest": "b7e8ad52629a1f1fca77a5c9a51da87ce2b9a81f6af9bcbe9bec9552d398e9bf"
+ },
+ {
+ "name": "u5408",
+ "unicode": "1F234",
+ "digest": "f359799d206cff6aae3af26eb8ad153abd38e817d4c70b2e5e5e8cf2f46e645e"
+ },
+ {
+ "name": "u55b6",
+ "unicode": "1F23A",
+ "digest": "c40293bea0f148e76ca5152e830b1b474380fe259180fbf74fece1ccc9afd8a3"
+ },
+ {
+ "name": "u6307",
+ "unicode": "1F22F",
+ "digest": "45449f7ae29da9e507c19d0f2b22f17f7cbd763f2ec87eb893be5bae49c7f78e"
+ },
+ {
+ "name": "u6708",
+ "unicode": "1F237",
+ "digest": "b897ead8c952013975ce6f381cdb8c584ebe4015311ef87f2a332c8a9e155d75"
+ },
+ {
+ "name": "u6709",
+ "unicode": "1F236",
+ "digest": "8b2f792abc1313a1a58f2fb8b37ad68a964004c962535f7739131257b1331a05"
+ },
+ {
+ "name": "u6e80",
+ "unicode": "1F235",
+ "digest": "fd982a56d4c492e63526b427bb948d7f155b0d5c414a68c7177698a71e72269b"
+ },
+ {
+ "name": "u7121",
+ "unicode": "1F21A",
+ "digest": "334f87a5254b58503d9f7a8ecc3d971a99839ec9c22c443469d72caca1750a48"
+ },
+ {
+ "name": "u7533",
+ "unicode": "1F238",
+ "digest": "3c8e743ae9960e43b9fa0cc698018fcb2a52ae34d143f0561298191f9def019c"
+ },
+ {
+ "name": "u7981",
+ "unicode": "1F232",
+ "digest": "a08bf39be3a54c076de79478c09b79c5c4d221853722870dd6e81abb78a4b64a"
+ },
+ {
+ "name": "u7a7a",
+ "unicode": "1F233",
+ "digest": "5dfb74a534a6490df989f84eac271c79d52f29313b6d43662dd0ff029794367c"
+ },
+ {
+ "name": "umbrella",
+ "unicode": "2614",
+ "digest": "ff1191f6c11b82f5337f78aadb58af50c69abaf676a384b0473bf49004e4018f"
+ },
+ {
+ "name": "umbrella2",
+ "unicode": "2602",
+ "digest": "aa7db9d6ed42dff847a8e5ee48a8eeff7a6e7f30de155a28951407f5aaa3dae2"
+ },
+ {
+ "name": "unamused",
+ "unicode": "1F612",
+ "digest": "efbbcaee6f3178afe509d74d13243ec6befe3112620a01e5079171eac4b32417"
+ },
+ {
+ "name": "underage",
+ "unicode": "1F51E",
+ "digest": "ae9a300fa400a57b7216a0a040fb8a5f02236fbceeeceed58bfd953c87ad51fe"
+ },
+ {
+ "name": "unicorn",
+ "unicode": "1F984",
+ "digest": "1b1e9c209dabe619db76fd346c3fb51b28ace0e4102697fe0973fe2d46aa9f08"
+ },
+ {
+ "name": "unlock",
+ "unicode": "1F513",
+ "digest": "63dbef0855399254ae01cf4ef0676adebc1432ae1ee260b569c23ae8152deaf8"
+ },
+ {
+ "name": "up",
+ "unicode": "1F199",
+ "digest": "902a3ecbcd73099a28476b49bc9e7b06da6cc002ee584e0501e5b625fb515088"
+ },
+ {
+ "name": "upside_down",
+ "unicode": "1F643",
+ "digest": "763fe2baf07a9b04f96958adf38a43c7dd2bc70d57398f49604307bd835cbb53"
+ },
+ {
+ "name": "urn",
+ "unicode": "26B1",
+ "digest": "dbfd5b90709d1b812d2fff71a5cfa10f84a4579866c2d7cd0e80759a22b2ba0e"
+ },
+ {
+ "name": "v",
+ "unicode": "270C",
+ "digest": "df85ad1a3ff365c3232a010701c9b25cd824d19fa2511422dee60ac231f457e3"
+ },
+ {
+ "name": "v_tone1",
+ "unicode": "270C-1F3FB",
+ "digest": "ce45db8de862b6f37d9208920d7c7c19335fac2cbff59b52be1ccbc01e3249da"
+ },
+ {
+ "name": "v_tone2",
+ "unicode": "270C-1F3FC",
+ "digest": "9036c8d793b02b4d2e6a4752b8ec319ec50efd6fcd6feef7b0671a63e5659acc"
+ },
+ {
+ "name": "v_tone3",
+ "unicode": "270C-1F3FD",
+ "digest": "a94b95f7656d62b442c99f2643b96b0c6114683401a94cdda68405c37efecc4c"
+ },
+ {
+ "name": "v_tone4",
+ "unicode": "270C-1F3FE",
+ "digest": "5c75f74993856f2faeeaee68df7689056e60d30e8c573039db8303167f7d0a80"
+ },
+ {
+ "name": "v_tone5",
+ "unicode": "270C-1F3FF",
+ "digest": "bb899672adb3c11f65983fbf9581de7f0a1bbac86fde146e799cea1126fe241e"
+ },
+ {
+ "name": "vertical_traffic_light",
+ "unicode": "1F6A6",
+ "digest": "36296e03620f16d35e5cec195cd97f5b358dfdedcd43bc1b3f7988ff7e85ab47"
+ },
+ {
+ "name": "vhs",
+ "unicode": "1F4FC",
+ "digest": "f4be55f4c23a85e0caacbf569742c117c8fd52c189465a6560cbd2f8873ad74f"
+ },
+ {
+ "name": "vibration_mode",
+ "unicode": "1F4F3",
+ "digest": "b9b8dfa3160c22f78b7d627cb52636d81ca6230a196cee5e94028e32e06b9a98"
+ },
+ {
+ "name": "video_camera",
+ "unicode": "1F4F9",
+ "digest": "3bfaa24e5fb00145e3e4dd07ecf569dabbb3f211551e46085ef23cf23002cfc3"
+ },
+ {
+ "name": "video_game",
+ "unicode": "1F3AE",
+ "digest": "4dcbd76030e37d0f7429852991a5f3f126cbdedfc124ecad0ba29d227375f6e2"
+ },
+ {
+ "name": "violin",
+ "unicode": "1F3BB",
+ "digest": "8ab7adc6e1e934f9e05009cd0a6d4da3136092c8f11c0606b91914be182206f5"
+ },
+ {
+ "name": "virgo",
+ "unicode": "264D",
+ "digest": "aaa19752756d0cac949445de1d2b8bf1f75a071368ae0acf5002f4acdc34826f"
+ },
+ {
+ "name": "volcano",
+ "unicode": "1F30B",
+ "digest": "86c17d61d66bfa868c02f1d31daca22f077c096368ef53cd9bfb9914a2f0b273"
+ },
+ {
+ "name": "volleyball",
+ "unicode": "1F3D0",
+ "digest": "b505684b13f814fbc08dc8ff652849328f46068276e0a24ae1961e2aff15868f"
+ },
+ {
+ "name": "vs",
+ "unicode": "1F19A",
+ "digest": "e31bd8b48b88c21d717964d1360a7751684dd1e0b63fdd655f1a9ec10a952dfb"
+ },
+ {
+ "name": "vulcan",
+ "unicode": "1F596",
+ "digest": "ca800fce797e652c5f47bf44992e8fbe19554688a36423fdf7c29ca6defae1e0"
+ },
+ {
+ "name": "vulcan_tone1",
+ "unicode": "1F596-1F3FB",
+ "digest": "84bafdaca43426b053f5caa4e868ca109d99113a28ea9799db09d3c5d5f645c8"
+ },
+ {
+ "name": "vulcan_tone2",
+ "unicode": "1F596-1F3FC",
+ "digest": "e7cedf63ead957ee5c287e4cb0828ba70673e17b604f92b529875c32d094e7e3"
+ },
+ {
+ "name": "vulcan_tone3",
+ "unicode": "1F596-1F3FD",
+ "digest": "e124fef20f289921553274cf834f6dcc1a012889d30d9874dc5ad01afb8235b8"
+ },
+ {
+ "name": "vulcan_tone4",
+ "unicode": "1F596-1F3FE",
+ "digest": "ea2115f549e4680467521bbf362b229f4a8f0fdadbfaf231378d801f9b369f08"
+ },
+ {
+ "name": "vulcan_tone5",
+ "unicode": "1F596-1F3FF",
+ "digest": "1b322e1252491f35ae02f0b279b6529dad867f2a6b3c2c3e77f981bed07e447d"
+ },
+ {
+ "name": "walking",
+ "unicode": "1F6B6",
+ "digest": "8ec0b2207d4368422261bc58944c17dff2554b2356becfb18f21dd87425cd67b"
+ },
+ {
+ "name": "walking_tone1",
+ "unicode": "1F6B6-1F3FB",
+ "digest": "9ee2224226326833fb0c9598c737fbd2f6bca1c81f082537e9f22ea1de4ff48e"
+ },
+ {
+ "name": "walking_tone2",
+ "unicode": "1F6B6-1F3FC",
+ "digest": "4855d521e937d10d58eeb2bbada493699e31e1098128f81a9e3303bcf3edeb49"
+ },
+ {
+ "name": "walking_tone3",
+ "unicode": "1F6B6-1F3FD",
+ "digest": "82669cf7167054a3615add01059f87dbb809edac3889ee171d5994de90448000"
+ },
+ {
+ "name": "walking_tone4",
+ "unicode": "1F6B6-1F3FE",
+ "digest": "c11f03aa96248272f831f68b93c5b21b2ecbffeb1b4c1c13373bf539ee7db8f8"
+ },
+ {
+ "name": "walking_tone5",
+ "unicode": "1F6B6-1F3FF",
+ "digest": "18238ee121a64211f6bcdbd475cee4ad6debe2bf421daba53d125aa005c26d10"
+ },
+ {
+ "name": "waning_crescent_moon",
+ "unicode": "1F318",
+ "digest": "96ef03ff85247877255a5ca3e8a8bb63f7d41f66531e8db61cbcd863e3ad7355"
+ },
+ {
+ "name": "waning_gibbous_moon",
+ "unicode": "1F316",
+ "digest": "994223113ad151e6b42ee317a10dad18f86759a308e61ab88eeb10ab780aae67"
+ },
+ {
+ "name": "warning",
+ "unicode": "26A0",
+ "digest": "a702e51efd1a3ab425eada008ccf694f38a71db14bb710edacc2e206d61f5ca3"
+ },
+ {
+ "name": "wastebasket",
+ "unicode": "1F5D1",
+ "digest": "afecb31aaf5078298ab9f7c5da29a49ce0cdefe477ee50889be9c0e43ccf1799"
+ },
+ {
+ "name": "watch",
+ "unicode": "231A",
+ "digest": "410334c87b8552f601f4ea1b7e36582a8b22f11b804d5ab1008d4af2b5a0cbe6"
+ },
+ {
+ "name": "water_buffalo",
+ "unicode": "1F403",
+ "digest": "d1becfaea464372c46e5442c6030ea355806ce5864c2435c123a9bb3a2c3c5eb"
+ },
+ {
+ "name": "watermelon",
+ "unicode": "1F349",
+ "digest": "88dd78812520c44080c79fe8cb1825bc713e5155da2ce8c73286333749e7035e"
+ },
+ {
+ "name": "wave",
+ "unicode": "1F44B",
+ "digest": "5103c49914ff1a2d76a1ab6db2530ddd9f48b98b708ab15292ceadf28873c939"
+ },
+ {
+ "name": "wave_tone1",
+ "unicode": "1F44B-1F3FB",
+ "digest": "ef2d79f377d09dedd1e900b2f4e4a2412bf562cd88484f71c52d465053f8aae9"
+ },
+ {
+ "name": "wave_tone2",
+ "unicode": "1F44B-1F3FC",
+ "digest": "d323e6e2e9ce035bc11b98226d46ab393dfdf3909d99e7a828b51950e6574656"
+ },
+ {
+ "name": "wave_tone3",
+ "unicode": "1F44B-1F3FD",
+ "digest": "8a8a386d53252455c20d6b235c462fd9cb3b20c9c19c67e67b3dece4621b5cf6"
+ },
+ {
+ "name": "wave_tone4",
+ "unicode": "1F44B-1F3FE",
+ "digest": "a8281c2ab9cf6e2b3d3cad24707fe412ec2398195530b716a2617477416c0432"
+ },
+ {
+ "name": "wave_tone5",
+ "unicode": "1F44B-1F3FF",
+ "digest": "5ccbee95bfc180580c8a02b88146110c4d132b8ea618dd6a58f03c1db921d58d"
+ },
+ {
+ "name": "wavy_dash",
+ "unicode": "3030",
+ "digest": "b5b67fc12938801a98ff22b6f7b566c603f58c183737fa740a500724879f0e99"
+ },
+ {
+ "name": "waxing_crescent_moon",
+ "unicode": "1F312",
+ "digest": "20446122d170b18f88ea71524f6747d42b97f9d765c52e676e5163fee58ec379"
+ },
+ {
+ "name": "waxing_gibbous_moon",
+ "unicode": "1F314",
+ "digest": "4324e43d4d45e6333f7379c9feb8efd3093d76f3920d7dc5ad3c615e76104998"
+ },
+ {
+ "name": "wc",
+ "unicode": "1F6BE",
+ "digest": "cb7c5d35bf11149d12cda2c0897cb6038e043127055bbe2e8e33c9b422d6d8fc"
+ },
+ {
+ "name": "weary",
+ "unicode": "1F629",
+ "digest": "29a291033a1b67eda3710dffae42d63fcfa663e37dab728c236172f3e877fe8f"
+ },
+ {
+ "name": "wedding",
+ "unicode": "1F492",
+ "digest": "6c7d874f464c9c76b0d767135aa40ced94089b5f71d373098b47488d7f3ef7c4"
+ },
+ {
+ "name": "whale",
+ "unicode": "1F433",
+ "digest": "94168acda6ba502b64ea50ff4aaafb7e6258d7c6806e91f090c8a3c46edc5b6d"
+ },
+ {
+ "name": "whale2",
+ "unicode": "1F40B",
+ "digest": "e1cde2308bd510b2449c96e88ffec796856f98b19ceedc1cd7e9ea009dae1417"
+ },
+ {
+ "name": "wheel_of_dharma",
+ "unicode": "2638",
+ "digest": "bbd6927697c22a1c3e56fd0c9933d9e00dbf120505fe48d02cb486bcd67a8b2c"
+ },
+ {
+ "name": "wheelchair",
+ "unicode": "267F",
+ "digest": "513f759acf528f6a7e39d9de1d171c3faebe645c9cf3bd86b185123016beef95"
+ },
+ {
+ "name": "white_check_mark",
+ "unicode": "2705",
+ "digest": "a0b3bf7c4fb131e7a9fab5169ea4094e2665e02cedaa091f0d6e78609b2f17ed"
+ },
+ {
+ "name": "white_circle",
+ "unicode": "26AA",
+ "digest": "2e7323fa4d1e3929e529d49210a0b82a043eae4f7c95128ec86b98c46fdb0e7c"
+ },
+ {
+ "name": "white_flower",
+ "unicode": "1F4AE",
+ "digest": "a3efea4950e09994f5e9d3d16f0728969238302304a6cce90b293c56e9a3e20c"
+ },
+ {
+ "name": "white_large_square",
+ "unicode": "2B1C",
+ "digest": "99c4442a65f2e3c568f45aed9e74590206c517a716557f4d741d967c9f42ed40"
+ },
+ {
+ "name": "white_medium_small_square",
+ "unicode": "25FD",
+ "digest": "a1edfeb4e540dcc020ba5dde19f7a18d90966788baa5382a22a0f9038d593f01"
+ },
+ {
+ "name": "white_medium_square",
+ "unicode": "25FB",
+ "digest": "794c2339ca71bb6d65ac488fb7b5dc4f0a2412f30890d2c4ece53cdbf52ba78b"
+ },
+ {
+ "name": "white_small_square",
+ "unicode": "25AB",
+ "digest": "9c4c308070a0c4524993cc36feaa778aad8f0df9f209b82d28b1f3811c441bc4"
+ },
+ {
+ "name": "white_square_button",
+ "unicode": "1F533",
+ "digest": "f46e18c7250c874d1b4d6117eda741d86a081352e76f3d019dd64af2669fa4bb"
+ },
+ {
+ "name": "white_sun_cloud",
+ "unicode": "1F325",
+ "digest": "d8ce416e6bdb0e59e06e2fceac3177dbe59fefc248fd8c6d76b80d1418141070"
+ },
+ {
+ "name": "white_sun_rain_cloud",
+ "unicode": "1F326",
+ "digest": "d2b132518261864ac4a95707eaeea335dd8351ed2b8ef4e2272ced456e309bf1"
+ },
+ {
+ "name": "white_sun_small_cloud",
+ "unicode": "1F324",
+ "digest": "b86a72f1cdb4d24fd3ab180aae9db012ca51fc01f3786aab596c2e330066b185"
+ },
+ {
+ "name": "wind_blowing_face",
+ "unicode": "1F32C",
+ "digest": "20bdeb8e39dc637792ac9fbee031c5791889f3126e83556ba51f98809c19763c"
+ },
+ {
+ "name": "wind_chime",
+ "unicode": "1F390",
+ "digest": "1fc26f33ce13b6a969bb76e914de054ec5d1c7c4cd1dc5ee8fea5f3149f794d8"
+ },
+ {
+ "name": "wine_glass",
+ "unicode": "1F377",
+ "digest": "7dfcf9c5195a20fd2745b19e102910392b0fc8f1650b98ab81957807841935e0"
+ },
+ {
+ "name": "wink",
+ "unicode": "1F609",
+ "digest": "404ac6c920414ca35894da1d97b3b2fabe92bd09569274eb5798fbb297129036"
+ },
+ {
+ "name": "wolf",
+ "unicode": "1F43A",
+ "digest": "ebadd7766c4a314b4027c32435a2f5727a6283123dfb8834e10251cbfc07ca2f"
+ },
+ {
+ "name": "woman",
+ "unicode": "1F469",
+ "digest": "9f0dbb5d1e0db4f008141582dcb6413f5aebaa13e191349c976a435b2bee0956"
+ },
+ {
+ "name": "woman_tone1",
+ "unicode": "1F469-1F3FB",
+ "digest": "c1f2a503481fdd96cfbfa7d556500f8e0da0cea1c72ed1078ecbb6962221c22a"
+ },
+ {
+ "name": "woman_tone2",
+ "unicode": "1F469-1F3FC",
+ "digest": "bf78b3a8f7424037069f8ac337e154ef185f55026c71a6cf6dbe15eb42ef9813"
+ },
+ {
+ "name": "woman_tone3",
+ "unicode": "1F469-1F3FD",
+ "digest": "4ccd70a2052b932b3395ac0a957c05815327dc8082fd461abcd797411db8ce05"
+ },
+ {
+ "name": "woman_tone4",
+ "unicode": "1F469-1F3FE",
+ "digest": "71b5efc4a410102e60048ca05f87587384a6db309f3be94109a4f92ea97072dc"
+ },
+ {
+ "name": "woman_tone5",
+ "unicode": "1F469-1F3FF",
+ "digest": "91a1cd015731f4db501c276a8236eb0665e4dc7aa1891e2a67b8d3e543fbea9c"
+ },
+ {
+ "name": "womans_clothes",
+ "unicode": "1F45A",
+ "digest": "599332c0b863a40fd0c319e4e0f52ae847326a96d180c288e0466b3ac308a27e"
+ },
+ {
+ "name": "womans_hat",
+ "unicode": "1F452",
+ "digest": "231ff55c3fa56d8fb5731fe41f547e67ffacfdde82286f45d4ca65a2d2821239"
+ },
+ {
+ "name": "womens",
+ "unicode": "1F6BA",
+ "digest": "f971429456b543804412490af2e27e0b14d0d536a156db898bce67b136e1b563"
+ },
+ {
+ "name": "worried",
+ "unicode": "1F61F",
+ "digest": "e017f636e79b9301f3a06471a5f3513ba7dbb9b97938de1140c1df4c32fd8844"
+ },
+ {
+ "name": "wrench",
+ "unicode": "1F527",
+ "digest": "c9ded4f7f496bad8691677226310bbd31bb485722ea479bc7a68a2b4ef9d55d9"
+ },
+ {
+ "name": "writing_hand",
+ "unicode": "1F58E",
+ "digest": "c4fc18ece6778339ebe14438aaf570e22385c3010c2d341824fa72ac6068cfeb"
+ },
+ {
+ "name": "writing_hand_tone1",
+ "unicode": "270D-1F3FB",
+ "digest": "38e64e6dca4847a12aef8a117c113b2025d841501c4bc8188c57d0c8a4f1e34d"
+ },
+ {
+ "name": "writing_hand_tone2",
+ "unicode": "270D-1F3FC",
+ "digest": "2b2d0ac2701ae707c31d9c85feb2e3700e11398701e2b0519338897817d53baf"
+ },
+ {
+ "name": "writing_hand_tone3",
+ "unicode": "270D-1F3FD",
+ "digest": "85d67f90ff8bd2e7157f28fd857e6730b660a7eb82eb5350f57671f728ce725b"
+ },
+ {
+ "name": "writing_hand_tone4",
+ "unicode": "270D-1F3FE",
+ "digest": "056c05c201b3d0972433f00910967ad7334e37726e2956fee053ec2e1a9153c7"
+ },
+ {
+ "name": "writing_hand_tone5",
+ "unicode": "270D-1F3FF",
+ "digest": "95c59157d301ee08990e4302fd9bdd7953e1d1abed09636d0837d84e44f53ba6"
+ },
+ {
+ "name": "x",
+ "unicode": "274C",
+ "digest": "1d256b0015b9cbdeaa4558f9241782c89d86c79a42e507621f7949c56a90b6c0"
+ },
+ {
+ "name": "yellow_heart",
+ "unicode": "1F49B",
+ "digest": "e869a80266b4379a8d82988fef25e187632bfb076ae619f576e416906cd688a7"
+ },
+ {
+ "name": "yen",
+ "unicode": "1F4B4",
+ "digest": "8f3d801c687e585e4497123c5c91a8b0c558578deec6a8c1591b25e64a3a8992"
+ },
+ {
+ "name": "yin_yang",
+ "unicode": "262F",
+ "digest": "e8ea4c686518ad6165e15ed67b529f2f1e20d648aa2ecb7e9bff5a6067dd3fea"
+ },
+ {
+ "name": "yum",
+ "unicode": "1F60B",
+ "digest": "d9c97bbf6bdb6e39977437680f0b37c9335306c51e01114056ae1d4c9c85b0e0"
+ },
+ {
+ "name": "zap",
+ "unicode": "26A1",
+ "digest": "37588734c7fe330ae35e6ee99e7cf4183e8fe1bc01f6bbbc6293b21076a338cb"
+ },
+ {
+ "name": "zero",
+ "unicode": "0030-20E3",
+ "digest": "519c927db8264d5379ab2c6a18656ea6dd1ceb2afc92eb48563bf86af4697571"
+ },
+ {
+ "name": "zipper_mouth",
+ "unicode": "1F910",
+ "digest": "8396249161b6d865861b56aabd17cae2c821b0d814f4249bf8cab0bb21fa8ee9"
+ },
+ {
+ "name": "zzz",
+ "unicode": "1F4A4",
+ "digest": "f07c56d2d55c0a886c26a8e3d49a9adeab54cc1a0c0354ea8d3bf23aaed3176d"
+ }
+] \ No newline at end of file
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 197e826e5bc..340fc5452ab 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -8,7 +8,7 @@ module API
expose :id, :state, :avatar_url
expose :web_url do |user, options|
- Gitlab::Application.routes.url_helpers.user_url(user)
+ Gitlab::Routing.url_helpers.user_url(user)
end
end
@@ -89,7 +89,7 @@ module API
expose :avatar_url
expose :web_url do |group, options|
- Gitlab::Application.routes.url_helpers.group_url(group)
+ Gitlab::Routing.url_helpers.group_url(group)
end
end
@@ -292,7 +292,7 @@ module API
end
class Label < Grape::Entity
- expose :name, :color
+ expose :name, :color, :description
end
class Compare < Grape::Entity
@@ -334,7 +334,6 @@ module API
expose :updated_at
expose :home_page_url
expose :default_branch_protection
- expose :twitter_sharing_enabled
expose :restricted_visibility_levels
expose :max_attachment_size
expose :session_expire_delay
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 78ca58ad0d1..4af6bef0fa7 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -17,17 +17,18 @@ module API
# Creates a new label
#
# Parameters:
- # id (required) - The ID of a project
- # name (required) - The name of the label to be deleted
- # color (required) - Color of the label given in 6-digit hex
- # notation with leading '#' sign (e.g. #FFAABB)
+ # id (required) - The ID of a project
+ # name (required) - The name of the label to be created
+ # color (required) - Color of the label given in 6-digit hex
+ # notation with leading '#' sign (e.g. #FFAABB)
+ # description (optional) - The description of label to be created
# Example Request:
# POST /projects/:id/labels
post ':id/labels' do
authorize! :admin_label, user_project
required_attributes! [:name, :color]
- attrs = attributes_for_keys [:name, :color]
+ attrs = attributes_for_keys [:name, :color, :description]
label = user_project.find_label(attrs[:name])
conflict!('Label already exists') if label
@@ -62,11 +63,12 @@ module API
# Updates an existing label. At least one optional parameter is required.
#
# Parameters:
- # id (required) - The ID of a project
- # name (required) - The name of the label to be deleted
- # new_name (optional) - The new name of the label
- # color (optional) - Color of the label given in 6-digit hex
- # notation with leading '#' sign (e.g. #FFAABB)
+ # id (required) - The ID of a project
+ # name (required) - The name of the label to be deleted
+ # new_name (optional) - The new name of the label
+ # color (optional) - Color of the label given in 6-digit hex
+ # notation with leading '#' sign (e.g. #FFAABB)
+ # description (optional) - The description of label to be created
# Example Request:
# PUT /projects/:id/labels
put ':id/labels' do
@@ -76,7 +78,7 @@ module API
label = user_project.find_label(params[:name])
not_found!('Label not found') unless label
- attrs = attributes_for_keys [:new_name, :color]
+ attrs = attributes_for_keys [:new_name, :color, :description]
if attrs.empty?
render_api_error!('Required parameters "new_name" or "color" ' \
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6fcb5261e40..24b31005475 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -244,6 +244,34 @@ module API
end
end
+ # Archive project
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # Example Request:
+ # PUT /projects/:id/archive
+ post ':id/archive' do
+ authorize!(:archive_project, user_project)
+
+ user_project.archive!
+
+ present user_project, with: Entities::Project
+ end
+
+ # Unarchive project
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # Example Request:
+ # PUT /projects/:id/unarchive
+ post ':id/unarchive' do
+ authorize!(:archive_project, user_project)
+
+ user_project.unarchive!
+
+ present user_project, with: Entities::Project
+ end
+
# Remove project
#
# Parameters:
diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb
index 783fcfb61ad..4fc3443ac68 100644
--- a/lib/award_emoji.rb
+++ b/lib/award_emoji.rb
@@ -48,4 +48,23 @@ class AwardEmoji
JSON.parse(File.read(json_path))
end
end
+
+ # Returns an Array of Emoji names and their asset URLs.
+ def self.urls
+ @urls ||= begin
+ path = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json')
+ prefix = Gitlab::Application.config.assets.prefix
+ digest = Gitlab::Application.config.assets.digest
+
+ JSON.parse(File.read(path)).map do |hash|
+ if digest
+ fname = "#{hash['unicode']}-#{hash['digest']}"
+ else
+ fname = hash['unicode']
+ end
+
+ { name: hash['name'], path: "#{prefix}/#{fname}.png" }
+ end
+ end
+ end
end
diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb
index 905c4c0144e..3eb544dfef9 100644
--- a/lib/banzai/filter.rb
+++ b/lib/banzai/filter.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/string/output_safety'
-
module Banzai
module Filter
def self.[](name)
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 34c38913474..f21dbef216c 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -11,15 +11,19 @@ module Banzai
end
def self.object_name
- object_class.name.underscore
+ @object_name ||= object_class.name.underscore
end
def self.object_sym
- object_name.to_sym
+ @object_sym ||= object_name.to_sym
end
def self.data_reference
- "data-#{object_name.dasherize}"
+ @data_reference ||= "data-#{object_name.dasherize}"
+ end
+
+ def self.object_class_title
+ @object_title ||= object_class.name.titleize
end
# Public: Find references in text (like `!123` for merge requests)
@@ -53,6 +57,10 @@ module Banzai
self.class.object_sym
end
+ def object_class_title
+ self.class.object_class_title
+ end
+
def references_in(*args, &block)
self.class.references_in(*args, &block)
end
@@ -62,36 +70,81 @@ module Banzai
# Example: project.merge_requests.find
end
+ def find_object_cached(project, id)
+ if RequestStore.active?
+ cache = find_objects_cache[object_class][project.id]
+
+ get_or_set_cache(cache, id) { find_object(project, id) }
+ else
+ find_object(project, id)
+ end
+ end
+
+ def project_from_ref_cache(ref)
+ if RequestStore.active?
+ cache = project_refs_cache
+
+ get_or_set_cache(cache, ref) { project_from_ref(ref) }
+ else
+ project_from_ref(ref)
+ end
+ end
+
def url_for_object(object, project)
# Implement in child class
# Example: project_merge_request_url
end
- def call
- if object_class.reference_pattern
- # `#123`
- replace_text_nodes_matching(object_class.reference_pattern) do |content|
- object_link_filter(content, object_class.reference_pattern)
- end
+ def url_for_object_cached(object, project)
+ if RequestStore.active?
+ cache = url_for_object_cache[object_class][project.id]
- # `[Issue](#123)`, which is turned into
- # `<a href="#123">Issue</a>`
- replace_link_nodes_with_href(object_class.reference_pattern) do |link, text|
- object_link_filter(link, object_class.reference_pattern, link_text: text)
- end
+ get_or_set_cache(cache, object) { url_for_object(object, project) }
+ else
+ url_for_object(object, project)
end
+ end
- if object_class.link_reference_pattern
- # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into
- # `<a href="http://gitlab.example.com/namespace/project/issues/123">http://gitlab.example.com/namespace/project/issues/123</a>`
- replace_link_nodes_with_text(object_class.link_reference_pattern) do |text|
- object_link_filter(text, object_class.link_reference_pattern)
- end
+ def call
+ return doc if project.nil?
+
+ ref_pattern = object_class.reference_pattern
+ link_pattern = object_class.link_reference_pattern
- # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into
- # `<a href="http://gitlab.example.com/namespace/project/issues/123">Issue</a>`
- replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text|
- object_link_filter(link, object_class.link_reference_pattern, link_text: text)
+ each_node do |node|
+ if text_node?(node) && ref_pattern
+ replace_text_when_pattern_matches(node, ref_pattern) do |content|
+ object_link_filter(content, ref_pattern)
+ end
+
+ elsif element_node?(node)
+ yield_valid_link(node) do |link, text|
+ if ref_pattern && link =~ /\A#{ref_pattern}/
+ replace_link_node_with_href(node, link) do
+ object_link_filter(link, ref_pattern, link_text: text)
+ end
+
+ next
+ end
+
+ next unless link_pattern
+
+ if link == text && text =~ /\A#{link_pattern}/
+ replace_link_node_with_text(node, link) do
+ object_link_filter(text, link_pattern)
+ end
+
+ next
+ end
+
+ if link =~ /\A#{link_pattern}\z/
+ replace_link_node_with_href(node, link) do
+ object_link_filter(link, link_pattern, link_text: text)
+ end
+
+ next
+ end
+ end
end
end
@@ -109,9 +162,9 @@ module Banzai
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def object_link_filter(text, pattern, link_text: nil)
references_in(text, pattern) do |match, id, project_ref, matches|
- project = project_from_ref(project_ref)
+ project = project_from_ref_cache(project_ref)
- if project && object = find_object(project, id)
+ if project && object = find_object_cached(project, id)
title = object_link_title(object)
klass = reference_class(object_sym)
@@ -121,8 +174,11 @@ module Banzai
object_sym => object.id
)
- url = matches[:url] if matches.names.include?("url")
- url ||= url_for_object(object, project)
+ if matches.names.include?("url") && matches[:url]
+ url = matches[:url]
+ else
+ url = url_for_object_cached(object, project)
+ end
text = link_text || object_link_text(object, matches)
@@ -146,7 +202,7 @@ module Banzai
end
def object_link_title(object)
- "#{object_class.name.titleize}: #{object.title}"
+ "#{object_class_title}: #{object.title}"
end
def object_link_text(object, matches)
@@ -157,6 +213,32 @@ module Banzai
text
end
+
+ private
+
+ def project_refs_cache
+ RequestStore[:banzai_project_refs] ||= {}
+ end
+
+ def find_objects_cache
+ RequestStore[:banzai_find_objects_cache] ||= Hash.new do |hash, key|
+ hash[key] = Hash.new { |h, k| h[k] = {} }
+ end
+ end
+
+ def url_for_object_cache
+ RequestStore[:banzai_url_for_object] ||= Hash.new do |hash, key|
+ hash[key] = Hash.new { |h, k| h[k] = {} }
+ end
+ end
+
+ def get_or_set_cache(cache, key)
+ if cache.key?(key)
+ cache[key]
+ else
+ cache[key] = yield
+ end
+ end
end
end
end
diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index 856f56fb175..fac7dad3243 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,4 +1,3 @@
-require 'html/pipeline/filter'
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index 470727ee312..b469ea0f626 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -43,7 +43,7 @@ module Banzai
end
def url_for_object(range, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_compare_url(project.namespace, project,
range.to_param.merge(only_path: context[:only_path]))
end
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index 713a56ba949..bd88207326c 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -37,7 +37,7 @@ module Banzai
end
def url_for_object(commit, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_commit_url(project.namespace, project, commit,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index 207437ba7cf..d25de900674 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,7 +1,3 @@
-require 'action_controller'
-require 'gitlab_emoji'
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML filter that replaces :emoji: with images.
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index edc26386903..37344b90576 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -35,15 +35,29 @@ module Banzai
def call
# Early return if the project isn't using an external tracker
- return doc if project.nil? || project.default_issues_tracker?
+ return doc if project.nil? || default_issues_tracker?
- replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content|
- issue_link_filter(content)
- end
+ ref_pattern = ExternalIssue.reference_pattern
+ ref_start_pattern = /\A#{ref_pattern}\z/
+
+ each_node do |node|
+ if text_node?(node)
+ replace_text_when_pattern_matches(node, ref_pattern) do |content|
+ issue_link_filter(content)
+ end
- replace_link_nodes_with_href(ExternalIssue.reference_pattern) do |link, text|
- issue_link_filter(link, link_text: text)
+ elsif element_node?(node)
+ yield_valid_link(node) do |link, text|
+ if link =~ ref_start_pattern
+ replace_link_node_with_href(node, link) do
+ issue_link_filter(link, link_text: text)
+ end
+ end
+ end
+ end
end
+
+ doc
end
# Replace `JIRA-123` issue references in text with links to the referenced
@@ -76,6 +90,21 @@ module Banzai
def url_for_issue(*args)
IssuesHelper.url_for_issue(*args)
end
+
+ def default_issues_tracker?
+ if RequestStore.active?
+ default_issues_tracker_cache[project.id] ||=
+ project.default_issues_tracker?
+ else
+ project.default_issues_tracker?
+ end
+ end
+
+ private
+
+ def default_issues_tracker_cache
+ RequestStore[:banzai_default_issues_tracker_cache] ||= {}
+ end
end
end
end
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index 8d368f3b9e7..d179bea181e 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML Filter to add a `rel="nofollow"` attribute to external links
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
index f31f921903b..7ce26db1b90 100644
--- a/lib/banzai/filter/gollum_tags_filter.rb
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -1,6 +1,3 @@
-require 'banzai'
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML Filter for parsing Gollum's tags in HTML. It's only parses the
diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb
new file mode 100644
index 00000000000..ccd106860bd
--- /dev/null
+++ b/lib/banzai/filter/image_link_filter.rb
@@ -0,0 +1,27 @@
+module Banzai
+ module Filter
+ # HTML filter that wraps links around inline images.
+ class ImageLinkFilter < HTML::Pipeline::Filter
+
+ # Find every image that isn't already wrapped in an `a` tag, create
+ # a new node (a link to the image source), copy the image as a child
+ # of the anchor, and then replace the img with the link-wrapped version.
+ def call
+ doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |img|
+
+ link = doc.document.create_element(
+ 'a',
+ class: 'no-attachment-icon',
+ href: img['src'],
+ target: '_blank'
+ )
+
+ link.children = img.clone
+ img.replace(link)
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index 8147e5ed3c7..a2987850d03 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -31,7 +31,7 @@ module Banzai
end
def url_for_object(label, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_issues_url(project.namespace, project, label_name: label.name,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index 0659fed1419..9b209533a89 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
class MarkdownFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 57c71708992..cad38a51851 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -14,7 +14,7 @@ module Banzai
end
def url_for_object(mr, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_merge_request_url(project.namespace, project, mr,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index e88b27c1fae..4cb82178024 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
module Banzai
module Filter
# HTML filter that replaces milestone references with links.
@@ -13,7 +11,7 @@ module Banzai
end
def url_for_object(issue, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_milestone_url(project.namespace, project, milestone,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index 7141ed7c9bd..e589b5df6ec 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML filter that removes references to records that the current user does
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 132f0a4bd93..31386cf851c 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,6 +1,3 @@
-require 'active_support/core_ext/string/output_safety'
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# Base class for GitLab Flavored Markdown reference filters.
@@ -55,18 +52,13 @@ module Banzai
html.html_safe? ? html : ERB::Util.html_escape_once(html)
end
- def ignore_parents
- @ignore_parents ||= begin
- # Don't look for references in text nodes that are children of these
- # elements.
+ def ignore_ancestor_query
+ @ignore_ancestor_query ||= begin
parents = %w(pre code a style)
parents << 'blockquote' if context[:ignore_blockquotes]
- parents.to_set
- end
- end
- def ignored_ancestry?(node)
- has_ancestor?(node, ignore_parents)
+ parents.map { |n| "ancestor::#{n}" }.join(' or ')
+ end
end
def project
@@ -77,119 +69,66 @@ module Banzai
"gfm gfm-#{type}"
end
- # Iterate through the document's text nodes, yielding the current node's
- # content if:
- #
- # * The `project` context value is present AND
- # * The node's content matches `pattern` AND
- # * The node is not an ancestor of an ignored node type
- #
- # pattern - Regex pattern against which to match the node's content
- #
- # Yields the current node's String contents. The result of the block will
- # replace the node's existing content and update the current document.
+ # Ensure that a :project key exists in context
#
- # Returns the updated Nokogiri::HTML::DocumentFragment object.
- def replace_text_nodes_matching(pattern)
- return doc if project.nil?
-
- search_text_nodes(doc).each do |node|
- next if ignored_ancestry?(node)
- next unless node.text =~ pattern
-
- content = node.to_html
-
- html = yield content
-
- next if html == content
-
- node.replace(html)
- end
-
- doc
+ # Note that while the key might exist, its value could be nil!
+ def validate
+ needs :project
end
- # Iterate through the document's link nodes, yielding the current node's
- # content if:
- #
- # * The `project` context value is present AND
- # * The node's content matches `pattern`
- #
- # pattern - Regex pattern against which to match the node's content
- #
- # Yields the current node's String contents. The result of the block will
- # replace the node and update the current document.
+ # Iterates over all <a> and text() nodes in a document.
#
- # Returns the updated Nokogiri::HTML::DocumentFragment object.
- def replace_link_nodes_with_text(pattern)
- return doc if project.nil?
+ # Nodes are skipped whenever their ancestor is one of the nodes returned
+ # by `ignore_ancestor_query`. Link tags are not processed if they have a
+ # "gfm" class or the "href" attribute is empty.
+ def each_node
+ query = %Q{descendant-or-self::text()[not(#{ignore_ancestor_query})]
+ | descendant-or-self::a[
+ not(contains(concat(" ", @class, " "), " gfm ")) and not(@href = "")
+ ]}
- doc.xpath('descendant-or-self::a').each do |node|
- klass = node.attr('class')
- next if klass && klass.include?('gfm')
-
- link = node.attr('href')
- text = node.text
-
- next unless link && text
-
- link = CGI.unescape(link)
- next unless link.force_encoding('UTF-8').valid_encoding?
- # Ignore ending punctionation like periods or commas
- next unless link == text && text =~ /\A#{pattern}/
-
- html = yield text
+ doc.xpath(query).each do |node|
+ yield node
+ end
+ end
- next if html == text
+ # Yields the link's URL and text whenever the node is a valid <a> tag.
+ def yield_valid_link(node)
+ link = CGI.unescape(node.attr('href').to_s)
+ text = node.text
- node.replace(html)
- end
+ return unless link.force_encoding('UTF-8').valid_encoding?
- doc
+ yield link, text
end
- # Iterate through the document's link nodes, yielding the current node's
- # content if:
- #
- # * The `project` context value is present AND
- # * The node's HREF matches `pattern`
- #
- # pattern - Regex pattern against which to match the node's HREF
- #
- # Yields the current node's String HREF and String content.
- # The result of the block will replace the node and update the current document.
- #
- # Returns the updated Nokogiri::HTML::DocumentFragment object.
- def replace_link_nodes_with_href(pattern)
- return doc if project.nil?
+ def replace_text_when_pattern_matches(node, pattern)
+ return unless node.text =~ pattern
- doc.xpath('descendant-or-self::a').each do |node|
- klass = node.attr('class')
- next if klass && klass.include?('gfm')
+ content = node.to_html
+ html = yield content
- link = node.attr('href')
- text = node.text
+ node.replace(html) unless content == html
+ end
- next unless link && text
- link = CGI.unescape(link)
- next unless link.force_encoding('UTF-8').valid_encoding?
- next unless link && link =~ /\A#{pattern}\z/
+ def replace_link_node_with_text(node, link)
+ html = yield
- html = yield link, text
+ node.replace(html) unless html == node.text
+ end
- next if html == link
+ def replace_link_node_with_href(node, link)
+ html = yield
- node.replace(html)
- end
+ node.replace(html) unless html == link
+ end
- doc
+ def text_node?(node)
+ node.is_a?(Nokogiri::XML::Text)
end
- # Ensure that a :project key exists in context
- #
- # Note that while the key might exist, its value could be nil!
- def validate
- needs :project
+ def element_node?(node)
+ node.is_a?(Nokogiri::XML::Element)
end
end
end
diff --git a/lib/banzai/filter/reference_gatherer_filter.rb b/lib/banzai/filter/reference_gatherer_filter.rb
index 86d484feb90..96fdb06304e 100644
--- a/lib/banzai/filter/reference_gatherer_filter.rb
+++ b/lib/banzai/filter/reference_gatherer_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML filter that gathers all referenced records that the current user has
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 41380627d39..ea21c7b041c 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,4 +1,3 @@
-require 'html/pipeline/filter'
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index e8011519608..42dbab9d27e 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,6 +1,3 @@
-require 'html/pipeline/filter'
-require 'html/pipeline/sanitization_filter'
-
module Banzai
module Filter
# Sanitize HTML
diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index c870a42f741..d507eb5ebe1 100644
--- a/lib/banzai/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -14,7 +14,7 @@ module Banzai
end
def url_for_object(snippet, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_snippet_url(project.namespace, project, snippet,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 8c5855e5ffc..62a79c62e20 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,4 +1,3 @@
-require 'html/pipeline/filter'
require 'rouge/plugins/redcarpet'
module Banzai
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index 4056dcd6d64..a4eda6fdf76 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML filter that adds an anchor child element to all Headers in a
diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb
index f642aee0967..7edfe5ade2d 100644
--- a/lib/banzai/filter/upload_link_filter.rb
+++ b/lib/banzai/filter/upload_link_filter.rb
@@ -1,4 +1,3 @@
-require 'html/pipeline/filter'
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index 24f16f8b547..eea3af842b6 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -59,13 +59,28 @@ module Banzai
end
def call
- replace_text_nodes_matching(User.reference_pattern) do |content|
- user_link_filter(content)
+ return doc if project.nil?
+
+ ref_pattern = User.reference_pattern
+ ref_pattern_start = /\A#{ref_pattern}\z/
+
+ each_node do |node|
+ if text_node?(node)
+ replace_text_when_pattern_matches(node, ref_pattern) do |content|
+ user_link_filter(content)
+ end
+ elsif element_node?(node)
+ yield_valid_link(node) do |link, text|
+ if link =~ ref_pattern_start
+ replace_link_node_with_href(node, link) do
+ user_link_filter(link, link_text: text)
+ end
+ end
+ end
+ end
end
- replace_link_nodes_with_href(User.reference_pattern) do |link, text|
- user_link_filter(link, link_text: text)
- end
+ doc
end
# Replace `@user` user references in text with links to the referenced
@@ -90,7 +105,7 @@ module Banzai
private
def urls
- Gitlab::Application.routes.url_helpers
+ Gitlab::Routing.url_helpers
end
def link_class
diff --git a/lib/banzai/filter/yaml_front_matter_filter.rb b/lib/banzai/filter/yaml_front_matter_filter.rb
index e4e2f3f228d..58e3e81209e 100644
--- a/lib/banzai/filter/yaml_front_matter_filter.rb
+++ b/lib/banzai/filter/yaml_front_matter_filter.rb
@@ -1,6 +1,3 @@
-require 'html/pipeline/filter'
-require 'yaml'
-
module Banzai
module Filter
class YamlFrontMatterFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/pipeline/base_pipeline.rb b/lib/banzai/pipeline/base_pipeline.rb
index f60966c3c0f..321fd5bbe14 100644
--- a/lib/banzai/pipeline/base_pipeline.rb
+++ b/lib/banzai/pipeline/base_pipeline.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline'
-
module Banzai
module Pipeline
class BasePipeline
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 8cd4b50e65a..ed3cfd6b023 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -7,6 +7,7 @@ module Banzai
Filter::SanitizationFilter,
Filter::UploadLinkFilter,
+ Filter::ImageLinkFilter,
Filter::EmojiFilter,
Filter::TableOfContentsFilter,
Filter::AutolinkFilter,
diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb
index 9b4ff0f0f80..0b5a9e0b2b8 100644
--- a/lib/banzai/pipeline/wiki_pipeline.rb
+++ b/lib/banzai/pipeline/wiki_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
module Banzai
module Pipeline
class WikiPipeline < FullPipeline
diff --git a/lib/gitlab/badge/build.rb b/lib/gitlab/badge/build.rb
new file mode 100644
index 00000000000..28a2391dbf8
--- /dev/null
+++ b/lib/gitlab/badge/build.rb
@@ -0,0 +1,24 @@
+module Gitlab
+ module Badge
+ ##
+ # Build badge
+ #
+ class Build
+ def initialize(project, ref)
+ @image = ::Ci::ImageForBuildService.new.execute(project, ref: ref)
+ end
+
+ def to_s
+ @image[:name].sub(/\.svg$/, '')
+ end
+
+ def type
+ 'image/svg+xml'
+ end
+
+ def data
+ File.read(@image[:path])
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 761b63e98f6..1acc22fe5bf 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -21,7 +21,6 @@ module Gitlab
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
- twitter_sharing_enabled: Settings.gitlab['twitter_sharing_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index 41f0edcaf7e..8f9be6cd9a3 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -5,7 +5,7 @@ module Gitlab
attr_accessor :recipient
attr_reader :author_id, :ref, :action
- include Gitlab::Application.routes.url_helpers
+ include Gitlab::Routing.url_helpers
delegate :namespace, :name_with_namespace, to: :project, prefix: :project
delegate :name, to: :author, prefix: :author
diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb
index d4b6f6d120d..97ef9851d71 100644
--- a/lib/gitlab/email/receiver.rb
+++ b/lib/gitlab/email/receiver.rb
@@ -63,6 +63,10 @@ module Gitlab
end
def reply_key
+ key_from_to_header || key_from_additional_headers
+ end
+
+ def key_from_to_header
key = nil
message.to.each do |address|
key = Gitlab::IncomingEmail.key_from_address(address)
@@ -72,6 +76,17 @@ module Gitlab
key
end
+ def key_from_additional_headers
+ reply_key = nil
+
+ Array(message.references).each do |message_id|
+ reply_key = Gitlab::IncomingEmail.key_from_fallback_reply_message_id(message_id)
+ break if reply_key
+ end
+
+ reply_key
+ end
+
def sent_notification
return nil unless reply_key
diff --git a/lib/gitlab/fogbugz_import/client.rb b/lib/gitlab/fogbugz_import/client.rb
index 431d50882fd..2152182b37f 100644
--- a/lib/gitlab/fogbugz_import/client.rb
+++ b/lib/gitlab/fogbugz_import/client.rb
@@ -26,7 +26,7 @@ module Gitlab
def user_map
users = {}
res = @api.command(:listPeople)
- res['people']['person'].each do |user|
+ [res['people']['person']].flatten.each do |user|
users[user['ixPerson']] = { name: user['sFullName'], email: user['sEmail'] }
end
users
diff --git a/lib/gitlab/gfm/reference_rewriter.rb b/lib/gitlab/gfm/reference_rewriter.rb
index a1c6ee7bd69..78d7a4f27cf 100644
--- a/lib/gitlab/gfm/reference_rewriter.rb
+++ b/lib/gitlab/gfm/reference_rewriter.rb
@@ -34,16 +34,21 @@ module Gitlab
@source_project = source_project
@current_user = current_user
@original_html = markdown(text)
+ @pattern = Gitlab::ReferenceExtractor.references_pattern
end
def rewrite(target_project)
- pattern = Gitlab::ReferenceExtractor.references_pattern
+ return @text unless needs_rewrite?
- @text.gsub(pattern) do |reference|
+ @text.gsub(@pattern) do |reference|
unfold_reference(reference, Regexp.last_match, target_project)
end
end
+ def needs_rewrite?
+ @text =~ @pattern
+ end
+
private
def unfold_reference(reference, match, target_project)
diff --git a/lib/gitlab/gfm/uploads_rewriter.rb b/lib/gitlab/gfm/uploads_rewriter.rb
new file mode 100644
index 00000000000..abc8c8c55e6
--- /dev/null
+++ b/lib/gitlab/gfm/uploads_rewriter.rb
@@ -0,0 +1,51 @@
+module Gitlab
+ module Gfm
+ ##
+ # Class that rewrites markdown links for uploads
+ #
+ # Using a pattern defined in `FileUploader` it copies files to a new
+ # project and rewrites all links to uploads in in a given text.
+ #
+ #
+ class UploadsRewriter
+ def initialize(text, source_project, _current_user)
+ @text = text
+ @source_project = source_project
+ @pattern = FileUploader::MARKDOWN_PATTERN
+ end
+
+ def rewrite(target_project)
+ return @text unless needs_rewrite?
+
+ @text.gsub(@pattern) do |markdown|
+ file = find_file(@source_project, $~[:secret], $~[:file])
+ return markdown unless file.try(:exists?)
+
+ new_uploader = FileUploader.new(target_project)
+ new_uploader.store!(file)
+ new_uploader.to_markdown
+ end
+ end
+
+ def needs_rewrite?
+ files.any?
+ end
+
+ def files
+ referenced_files = @text.scan(@pattern).map do
+ find_file(@source_project, $~[:secret], $~[:file])
+ end
+
+ referenced_files.compact.select(&:exists?)
+ end
+
+ private
+
+ def find_file(project, secret, file)
+ uploader = FileUploader.new(project, secret)
+ uploader.retrieve_from_store!(file)
+ uploader.file
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb
index 9068d79c95e..8ce9d32abe0 100644
--- a/lib/gitlab/incoming_email.rb
+++ b/lib/gitlab/incoming_email.rb
@@ -1,13 +1,10 @@
module Gitlab
module IncomingEmail
class << self
- def enabled?
- config.enabled && address_formatted_correctly?
- end
+ FALLBACK_REPLY_MESSAGE_ID_REGEX = /\Areply\-(.+)@#{Gitlab.config.gitlab.host}\Z/.freeze
- def address_formatted_correctly?
- config.address &&
- config.address.include?("%{key}")
+ def enabled?
+ config.enabled && config.address
end
def reply_address(key)
@@ -24,6 +21,13 @@ module Gitlab
match[1]
end
+ def key_from_fallback_reply_message_id(message_id)
+ match = message_id.match(FALLBACK_REPLY_MESSAGE_ID_REGEX)
+ return unless match
+
+ match[1]
+ end
+
def config
Gitlab.config.incoming_email
end
diff --git a/lib/gitlab/note_data_builder.rb b/lib/gitlab/note_data_builder.rb
index 71cf6a0d886..18523e0aefe 100644
--- a/lib/gitlab/note_data_builder.rb
+++ b/lib/gitlab/note_data_builder.rb
@@ -41,7 +41,7 @@ module Gitlab
data[:issue] = note.noteable.hook_attrs
elsif note.for_merge_request?
data[:merge_request] = note.noteable.hook_attrs
- elsif note.for_project_snippet?
+ elsif note.for_snippet?
data[:snippet] = note.noteable.hook_attrs
end
diff --git a/lib/gitlab/routing.rb b/lib/gitlab/routing.rb
new file mode 100644
index 00000000000..5132177de51
--- /dev/null
+++ b/lib/gitlab/routing.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module Routing
+ # Returns the URL helpers Module.
+ #
+ # This method caches the output as Rails' "url_helpers" method creates an
+ # anonymous module every time it's called.
+ #
+ # Returns a Module.
+ def self.url_helpers
+ @url_helpers ||= Gitlab::Application.routes.url_helpers
+ end
+ end
+end
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index 6f0d02cafd1..f301d42939d 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -1,7 +1,8 @@
module Gitlab
class UrlBuilder
- include Gitlab::Application.routes.url_helpers
+ include Gitlab::Routing.url_helpers
include GitlabRoutingHelper
+ include ActionView::RecordIdentifier
def initialize(type)
@type = type
@@ -37,19 +38,16 @@ module Gitlab
namespace_project_commit_url(namespace_id: note.project.namespace,
id: note.commit_id,
project_id: note.project,
- anchor: "note_#{note.id}")
+ anchor: dom_id(note))
elsif note.for_issue?
issue = Issue.find(note.noteable_id)
- issue_url(issue,
- anchor: "note_#{note.id}")
+ issue_url(issue, anchor: dom_id(note))
elsif note.for_merge_request?
merge_request = MergeRequest.find(note.noteable_id)
- merge_request_url(merge_request,
- anchor: "note_#{note.id}")
- elsif note.for_project_snippet?
+ merge_request_url(merge_request, anchor: dom_id(note))
+ elsif note.for_snippet?
snippet = Snippet.find(note.noteable_id)
- project_snippet_url(snippet,
- anchor: "note_#{note.id}")
+ project_snippet_url(snippet, anchor: dom_id(note))
end
end
end
diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake
index cfaf4a129b1..7ec00a898fd 100644
--- a/lib/tasks/gemojione.rake
+++ b/lib/tasks/gemojione.rake
@@ -1,19 +1,39 @@
-# This task will generate a standard and Retina sprite of all of the current
-# Gemojione Emojis, with the accompanying SCSS map.
-#
-# It will not appear in `rake -T` output, and the dependent gems are not
-# included in the Gemfile by default, because this task will only be needed
-# occasionally, such as when new Emojis are added to Gemojione.
-
-begin
- require 'sprite_factory'
- require 'rmagick'
-rescue LoadError
- # noop
-end
-
namespace :gemojione do
+ desc 'Generates Emoji SHA256 digests'
+ task digests: :environment do
+ require 'digest/sha2'
+ require 'json'
+
+ dir = Gemojione.index.images_path
+
+ digests = AwardEmoji.emojis.map do |name, emoji_hash|
+ fpath = File.join(dir, "#{emoji_hash['unicode']}.png")
+ digest = Digest::SHA256.file(fpath).hexdigest
+
+ { name: name, unicode: emoji_hash['unicode'], digest: digest }
+ end
+
+ out = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json')
+
+ File.open(out, 'w') do |handle|
+ handle.write(JSON.pretty_generate(digests))
+ end
+ end
+
+ # This task will generate a standard and Retina sprite of all of the current
+ # Gemojione Emojis, with the accompanying SCSS map.
+ #
+ # It will not appear in `rake -T` output, and the dependent gems are not
+ # included in the Gemfile by default, because this task will only be needed
+ # occasionally, such as when new Emojis are added to Gemojione.
task sprite: :environment do
+ begin
+ require 'sprite_factory'
+ require 'rmagick'
+ rescue LoadError
+ # noop
+ end
+
check_requirements!
SIZE = 20
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 27ed57efe55..effb8eb6001 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -623,7 +623,6 @@ namespace :gitlab do
start_checking "Reply by email"
if Gitlab.config.incoming_email.enabled
- check_address_formatted_correctly
check_imap_authentication
if Rails.env.production?
@@ -643,20 +642,6 @@ namespace :gitlab do
# Checks
########################
- def check_address_formatted_correctly
- print "Address formatted correctly? ... "
-
- if Gitlab::IncomingEmail.address_formatted_correctly?
- puts "yes".green
- else
- puts "no".red
- try_fixing_it(
- "Make sure that the address in config/gitlab.yml includes the '%{key}' placeholder."
- )
- fix_and_rerun
- end
- end
-
def check_initd_configured_correctly
print "Init.d configured correctly? ... "
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 5b1f65d7aff..9ef8ba1b097 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -1,15 +1,14 @@
require 'spec_helper'
describe Admin::UsersController do
- let(:admin) { create(:admin) }
+ let(:user) { create(:user) }
before do
- sign_in(admin)
+ sign_in(create(:admin))
end
describe 'DELETE #user with projects' do
- let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:empty_project, namespace: user.namespace) }
before do
project.team << [user, :developer]
@@ -23,8 +22,6 @@ describe Admin::UsersController do
end
describe 'PUT block/:id' do
- let(:user) { create(:user) }
-
it 'blocks user' do
put :block, id: user.username
user.reload
@@ -50,8 +47,6 @@ describe Admin::UsersController do
end
context 'manually blocked users' do
- let(:user) { create(:user) }
-
before do
user.block
end
@@ -66,8 +61,6 @@ describe Admin::UsersController do
end
describe 'PUT unlock/:id' do
- let(:user) { create(:user) }
-
before do
request.env["HTTP_REFERER"] = "/"
user.lock_access!
@@ -95,8 +88,6 @@ describe Admin::UsersController do
end
describe 'PATCH disable_two_factor' do
- let(:user) { create(:user) }
-
it 'disables 2FA for the user' do
expect(user).to receive(:disable_two_factor!)
allow(subject).to receive(:user).and_return(user)
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index c5b034dc064..75e6b6f45a7 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -63,7 +63,7 @@ describe Projects::MergeRequestsController do
id: merge_request.iid,
format: format)
- expect(response.body).to eq((merge_request.send(:"to_#{format}",user)).to_s)
+ expect(response.body).to eq((merge_request.send(:"to_#{format}")).to_s)
end
it "should not escape Html" do
diff --git a/spec/factories/file_uploader.rb b/spec/factories/file_uploader.rb
new file mode 100644
index 00000000000..1b36e21f2b0
--- /dev/null
+++ b/spec/factories/file_uploader.rb
@@ -0,0 +1,20 @@
+FactoryGirl.define do
+ factory :file_uploader do
+ project
+ secret nil
+
+ transient do
+ fixture { 'rails_sample.jpg' }
+ path { File.join(Rails.root, 'spec/fixtures', fixture) }
+ file { Rack::Test::UploadedFile.new(path) }
+ end
+
+ after(:build) do |uploader, evaluator|
+ uploader.store!(evaluator.file)
+ end
+
+ initialize_with do
+ new(project, secret)
+ end
+ end
+end
diff --git a/spec/factories/forked_project_links.rb b/spec/factories/forked_project_links.rb
index 252bf2747e1..19a54946fe0 100644
--- a/spec/factories/forked_project_links.rb
+++ b/spec/factories/forked_project_links.rb
@@ -13,5 +13,10 @@ FactoryGirl.define do
factory :forked_project_link do
association :forked_to_project, factory: :project
association :forked_from_project, factory: :project
+
+ after(:create) do |link|
+ link.forked_from_project.reload
+ link.forked_to_project.reload
+ end
end
end
diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb
index 457859dedaf..62de081661d 100644
--- a/spec/factories_spec.rb
+++ b/spec/factories_spec.rb
@@ -1,9 +1,17 @@
require 'spec_helper'
-FactoryGirl.factories.map(&:name).each do |factory_name|
- describe "#{factory_name} factory" do
- it 'should be valid' do
- expect(build(factory_name)).to be_valid
+describe 'factories' do
+ FactoryGirl.factories.each do |factory|
+ describe "#{factory.name} factory" do
+ let(:entity) { build(factory.name) }
+
+ it 'does not raise error when created 'do
+ expect { entity }.to_not raise_error
+ end
+
+ it 'should be valid', if: factory.build_class < ActiveRecord::Base do
+ expect(entity).to be_valid
+ end
end
end
end
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index dc41be8246f..de6aed74fb4 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -61,7 +61,7 @@ describe "User Feed", feature: true do
end
it 'should have XHTML summaries in merge request descriptions' do
- expect(body).to match /Here is the fix: <img[^>]*\/>/
+ expect(body).to match /Here is the fix: <a[^>]*><img[^>]*\/><\/a>/
end
end
end
diff --git a/spec/features/issues/filter_by_milestone_spec.rb b/spec/features/issues/filter_by_milestone_spec.rb
index d8e2ecb9feb..99445185893 100644
--- a/spec/features/issues/filter_by_milestone_spec.rb
+++ b/spec/features/issues/filter_by_milestone_spec.rb
@@ -11,7 +11,41 @@ feature 'Issue filtering by Milestone', feature: true do
visit_issues(project)
filter_by_milestone(Milestone::None.title)
- expect(page).to have_css('.issue .title', count: 1)
+ expect(page).to have_css('.issue', count: 1)
+ end
+
+ context 'filters by upcoming milestone', js: true do
+ it 'should not show issues with no expiry' do
+ create(:issue, project: project)
+ create(:issue, project: project, milestone: milestone)
+
+ visit_issues(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.issue', count: 0)
+ end
+
+ it 'should show issues in future' do
+ milestone = create(:milestone, project: project, due_date: Date.tomorrow)
+ create(:issue, project: project)
+ create(:issue, project: project, milestone: milestone)
+
+ visit_issues(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.issue', count: 1)
+ end
+
+ it 'should not show issues in past' do
+ milestone = create(:milestone, project: project, due_date: Date.yesterday)
+ create(:issue, project: project)
+ create(:issue, project: project, milestone: milestone)
+
+ visit_issues(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.issue', count: 0)
+ end
end
scenario 'filters by a specific Milestone', js: true do
@@ -21,7 +55,7 @@ feature 'Issue filtering by Milestone', feature: true do
visit_issues(project)
filter_by_milestone(milestone.title)
- expect(page).to have_css('.issue .title', count: 1)
+ expect(page).to have_css('.issue', count: 1)
end
def visit_issues(project)
@@ -30,8 +64,6 @@ feature 'Issue filtering by Milestone', feature: true do
def filter_by_milestone(title)
find(".js-milestone-select").click
- sleep 0.5
find(".milestone-filter .dropdown-content a", text: title).click
- sleep 1
end
end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
new file mode 100644
index 00000000000..fd02d584848
--- /dev/null
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+feature 'Create New Merge Request', feature: true, js: false do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+
+ before do
+ project.team << [user, :master]
+
+ login_as user
+ visit namespace_project_merge_requests_path(project.namespace, project)
+ end
+
+ it 'generates a diff for an orphaned branch' do
+ click_link 'New Merge Request'
+ select "orphaned-branch", from: "merge_request_source_branch"
+ select "master", from: "merge_request_target_branch"
+ click_button "Compare branches"
+
+ expect(page).to have_content "README.md"
+ expect(page).to have_content "wm.png"
+
+ fill_in "merge_request_title", with: "Orphaned MR test"
+ click_button "Submit merge request"
+
+ expect(page).to have_content 'git checkout -b orphaned-branch origin/orphaned-branch'
+ end
+end
diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb
index b76e4c74c79..c57ab5f3b03 100644
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ b/spec/features/merge_requests/filter_by_milestone_spec.rb
@@ -11,7 +11,41 @@ feature 'Merge Request filtering by Milestone', feature: true do
visit_merge_requests(project)
filter_by_milestone(Milestone::None.title)
- expect(page).to have_css('.merge-request-title', count: 1)
+ expect(page).to have_css('.merge-request', count: 1)
+ end
+
+ context 'filters by upcoming milestone', js: true do
+ it 'should not show issues with no expiry' do
+ create(:merge_request, :with_diffs, source_project: project)
+ create(:merge_request, :simple, source_project: project, milestone: milestone)
+
+ visit_merge_requests(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.merge-request', count: 0)
+ end
+
+ it 'should show issues in future' do
+ milestone = create(:milestone, project: project, due_date: Date.tomorrow)
+ create(:merge_request, :with_diffs, source_project: project)
+ create(:merge_request, :simple, source_project: project, milestone: milestone)
+
+ visit_merge_requests(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.merge-request', count: 1)
+ end
+
+ it 'should not show issues in past' do
+ milestone = create(:milestone, project: project, due_date: Date.yesterday)
+ create(:merge_request, :with_diffs, source_project: project)
+ create(:merge_request, :simple, source_project: project, milestone: milestone)
+
+ visit_merge_requests(project)
+ filter_by_milestone(Milestone::Upcoming.title)
+
+ expect(page).to have_css('.merge-request', count: 0)
+ end
end
scenario 'filters by a specific Milestone', js: true do
@@ -21,7 +55,7 @@ feature 'Merge Request filtering by Milestone', feature: true do
visit_merge_requests(project)
filter_by_milestone(milestone.title)
- expect(page).to have_css('.merge-request-title', count: 1)
+ expect(page).to have_css('.merge-request', count: 1)
end
def visit_merge_requests(project)
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 84c036e59c0..3e6289a46b1 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -1,19 +1,46 @@
require 'spec_helper'
describe "Search", feature: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+
before do
- login_as :user
- @project = create(:project, namespace: @user.namespace)
- @project.team << [@user, :reporter]
+ login_with(user)
+ project.team << [user, :reporter]
visit search_path
+ end
- page.within '.search-holder' do
- fill_in "search", with: @project.name[0..3]
- click_button "Search"
+ describe 'searching for Projects' do
+ it 'finds a project' do
+ page.within '.search-holder' do
+ fill_in "search", with: project.name[0..3]
+ click_button "Search"
+ end
+
+ expect(page).to have_content project.name
end
end
- it "should show project in search results" do
- expect(page).to have_content @project.name
+ context 'search for comments' do
+ it 'finds a snippet' do
+ snippet = create(:project_snippet, :private, project: project, author: user, title: 'Some title')
+ note = create(:note,
+ noteable: snippet,
+ author: user,
+ note: 'Supercalifragilisticexpialidocious',
+ project: project)
+ # Must visit project dashboard since global search won't search
+ # everything (e.g. comments, snippets, etc.)
+ visit namespace_project_path(project.namespace, project)
+
+ page.within '.search' do
+ fill_in 'search', with: note.note
+ click_button 'Go'
+ end
+
+ click_link 'Comments'
+
+ expect(page).to have_link(snippet.title)
+ end
end
end
diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
new file mode 100644
index 00000000000..39d5cefbc2a
--- /dev/null
+++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml
@@ -0,0 +1,42 @@
+Return-Path: <jake@adventuretime.ooo>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <reply@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: Jake the Dog <jake@adventuretime.ooo>
+To: reply@appmail.adventuretime.ooo
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+In-Reply-To: <issue_1@localhost>
+References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
+Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
+I could not disagree more. I am obviously biased but adventure time is the
+greatest show ever created. Everyone should watch it.
+
+- Jake out
+
+
+On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta
+<reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo> wrote:
+>
+>
+>
+> eviltrout posted in 'Adventure Time Sux' on Discourse Meta:
+>
+> ---
+> hey guys everyone knows adventure time sucks!
+>
+> ---
+> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3
+>
+> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences).
+>
diff --git a/spec/fixtures/emails/valid_reply.eml b/spec/fixtures/emails/valid_reply.eml
index 1e696389954..980e10a8812 100644
--- a/spec/fixtures/emails/valid_reply.eml
+++ b/spec/fixtures/emails/valid_reply.eml
@@ -7,6 +7,8 @@ Date: Thu, 13 Jun 2013 17:03:48 -0400
From: Jake the Dog <jake@adventuretime.ooo>
To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo
Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+In-Reply-To: <issue_1@localhost>
+References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>
Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux'
Mime-Version: 1.0
Content-Type: text/plain;
@@ -37,4 +39,4 @@ On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta
> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3
>
> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences).
-> \ No newline at end of file
+>
diff --git a/spec/lib/award_emoji_spec.rb b/spec/lib/award_emoji_spec.rb
new file mode 100644
index 00000000000..330678f7f16
--- /dev/null
+++ b/spec/lib/award_emoji_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe AwardEmoji do
+ describe '.urls' do
+ subject { AwardEmoji.urls }
+
+ it { is_expected.to be_an_instance_of(Array) }
+ it { is_expected.to_not be_empty }
+
+ context 'every Hash in the Array' do
+ it 'has the correct keys and values' do
+ subject.each do |hash|
+ expect(hash[:name]).to be_an_instance_of(String)
+ expect(hash[:path]).to be_an_instance_of(String)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/image_link_filter_spec.rb b/spec/lib/banzai/filter/image_link_filter_spec.rb
new file mode 100644
index 00000000000..dd5594750c8
--- /dev/null
+++ b/spec/lib/banzai/filter/image_link_filter_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Banzai::Filter::ImageLinkFilter, lib: true do
+ include FilterSpecHelper
+
+ def image(path)
+ %(<img src="#{path}" />)
+ end
+
+ it 'wraps the image with a link to the image src' do
+ doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+ expect(doc.at_css('img')['src']).to eq doc.at_css('a')['href']
+ end
+
+ it 'does not wrap a duplicate link' do
+ exp = act = %q(<a href="/whatever">#{image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')}</a>)
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'works with external images' do
+ doc = filter(image('https://i.imgur.com/DfssX9C.jpg'))
+ expect(doc.at_css('img')['src']).to eq doc.at_css('a')['href']
+ end
+end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index f38fadda9ba..566035c60d0 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe ExtractsPath, lib: true do
include ExtractsPath
include RepoHelpers
- include Gitlab::Application.routes.url_helpers
+ include Gitlab::Routing.url_helpers
let(:project) { double('project') }
diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb
new file mode 100644
index 00000000000..b78c2b6224f
--- /dev/null
+++ b/spec/lib/gitlab/badge/build_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+describe Gitlab::Badge::Build do
+ let(:project) { create(:project) }
+ let(:sha) { project.commit.sha }
+ let(:badge) { described_class.new(project, 'master') }
+
+ describe '#type' do
+ subject { badge.type }
+ it { is_expected.to eq 'image/svg+xml' }
+ end
+
+ context 'build exists' do
+ let(:ci_commit) { create(:ci_commit, project: project, sha: sha) }
+ let!(:build) { create(:ci_build, commit: ci_commit) }
+
+
+ context 'build success' do
+ before { build.success! }
+
+ describe '#to_s' do
+ subject { badge.to_s }
+ it { is_expected.to eq 'build-success' }
+ end
+
+ describe '#data' do
+ let(:data) { badge.data }
+
+ it 'contains infromation about success' do
+ expect(status_node(data, 'success')).to be_truthy
+ end
+ end
+ end
+
+ context 'build failed' do
+ before { build.drop! }
+
+ describe '#to_s' do
+ subject { badge.to_s }
+ it { is_expected.to eq 'build-failed' }
+ end
+
+ describe '#data' do
+ let(:data) { badge.data }
+
+ it 'contains infromation about failure' do
+ expect(status_node(data, 'failed')).to be_truthy
+ end
+ end
+ end
+ end
+
+ context 'build does not exist' do
+ describe '#to_s' do
+ subject { badge.to_s }
+ it { is_expected.to eq 'build-unknown' }
+ end
+
+ describe '#data' do
+ let(:data) { badge.data }
+
+ it 'contains infromation about unknown build' do
+ expect(status_node(data, 'unknown')).to be_truthy
+ end
+ end
+ end
+
+ def status_node(data, status)
+ xml = Nokogiri::XML.parse(data)
+ xml.at(%Q{text:contains("#{status}")})
+ end
+end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 844fd79c991..a1f51429a79 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -236,6 +236,6 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
end
def urls
- Gitlab::Application.routes.url_helpers
+ Gitlab::Routing.url_helpers
end
end
diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb
index abe179cd4af..36267faeb93 100644
--- a/spec/lib/gitlab/email/receiver_spec.rb
+++ b/spec/lib/gitlab/email/receiver_spec.rb
@@ -3,6 +3,7 @@ require "spec_helper"
describe Gitlab::Email::Receiver, lib: true do
before do
stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo")
+ stub_config_setting(host: 'localhost')
end
let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" }
@@ -137,5 +138,27 @@ describe Gitlab::Email::Receiver, lib: true do
expect(note.note).to include(markdown)
end
+
+ context 'when sub-addressing is not supported' do
+ before do
+ stub_incoming_email_setting(enabled: true, address: nil)
+ end
+
+ shared_examples 'an email that contains a reply key' do |header|
+ it "fetches the reply key from the #{header} header and creates a comment" do
+ expect { receiver.execute }.to change { noteable.notes.count }.by(1)
+ note = noteable.notes.last
+
+ expect(note.author).to eq(sent_notification.recipient)
+ expect(note.note).to include('I could not disagree more.')
+ end
+ end
+
+ context 'reply key is in the References header' do
+ let(:email_raw) { fixture_file('emails/reply_without_subaddressing_and_key_inside_references.eml') }
+
+ it_behaves_like 'an email that contains a reply key', 'References'
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/fogbugz_import/client_spec.rb b/spec/lib/gitlab/fogbugz_import/client_spec.rb
new file mode 100644
index 00000000000..2dc71be0254
--- /dev/null
+++ b/spec/lib/gitlab/fogbugz_import/client_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Gitlab::FogbugzImport::Client, lib: true do
+
+ let(:client) { described_class.new(uri: '', token: '') }
+ let(:one_user) { { 'people' => { 'person' => { "ixPerson" => "2", "sFullName" => "James" } } } }
+ let(:two_users) { { 'people' => { 'person' => [one_user, { "ixPerson" => "3" }] } } }
+
+ it 'retrieves user_map with one user' do
+ stub_api(one_user)
+
+ expect(client.user_map.count).to eq(1)
+ end
+
+ it 'retrieves user_map with two users' do
+ stub_api(two_users)
+
+ expect(client.user_map.count).to eq(2)
+ end
+
+ def stub_api(users)
+ allow_any_instance_of(::Fogbugz::Interface).to receive(:command).with(:listPeople).and_return(users)
+ end
+end
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
new file mode 100644
index 00000000000..eda956e6f0a
--- /dev/null
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -0,0 +1,66 @@
+require 'spec_helper'
+
+describe Gitlab::Gfm::UploadsRewriter do
+ let(:user) { create(:user) }
+ let(:old_project) { create(:project) }
+ let(:new_project) { create(:project) }
+ let(:rewriter) { described_class.new(text, old_project, user) }
+
+ context 'text contains links to uploads' do
+ let(:image_uploader) do
+ build(:file_uploader, project: old_project)
+ end
+
+ let(:zip_uploader) do
+ build(:file_uploader, project: old_project,
+ fixture: 'ci_build_artifacts.zip')
+ end
+
+ let(:text) do
+ "Text and #{image_uploader.to_markdown} and #{zip_uploader.to_markdown}"
+ end
+
+ describe '#rewrite' do
+ let!(:new_text) { rewriter.rewrite(new_project) }
+
+ let(:old_files) { [image_uploader, zip_uploader].map(&:file) }
+ let(:new_files) do
+ described_class.new(new_text, new_project, user).files
+ end
+
+ let(:old_paths) { old_files.map(&:path) }
+ let(:new_paths) { new_files.map(&:path) }
+
+ it 'rewrites content' do
+ expect(new_text).to_not eq text
+ expect(new_text.length).to eq text.length
+ end
+
+ it 'copies files' do
+ expect(new_files).to all(exist)
+ expect(old_paths).to_not match_array new_paths
+ expect(old_paths).to all(include(old_project.path_with_namespace))
+ expect(new_paths).to all(include(new_project.path_with_namespace))
+ end
+
+ it 'does not remove old files' do
+ expect(old_files).to all(exist)
+ end
+
+ it 'generates a new secret for each file' do
+ expect(new_paths).to_not include image_uploader.secret
+ expect(new_paths).to_not include zip_uploader.secret
+ end
+ end
+
+ describe '#needs_rewrite?' do
+ subject { rewriter.needs_rewrite? }
+ it { is_expected.to eq true }
+ end
+
+ describe '#files' do
+ subject { rewriter.files }
+ it { is_expected.to be_an(Array) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb
index bcdba8d4c12..afb3e26f8fb 100644
--- a/spec/lib/gitlab/incoming_email_spec.rb
+++ b/spec/lib/gitlab/incoming_email_spec.rb
@@ -7,24 +7,8 @@ describe Gitlab::IncomingEmail, lib: true do
stub_incoming_email_setting(enabled: true)
end
- context "when the address is valid" do
- before do
- stub_incoming_email_setting(address: "replies+%{key}@example.com")
- end
-
- it "returns true" do
- expect(described_class.enabled?).to be_truthy
- end
- end
-
- context "when the address is invalid" do
- before do
- stub_incoming_email_setting(address: "replies@example.com")
- end
-
- it "returns false" do
- expect(described_class.enabled?).to be_falsey
- end
+ it 'returns true' do
+ expect(described_class.enabled?).to be_truthy
end
end
@@ -58,4 +42,10 @@ describe Gitlab::IncomingEmail, lib: true do
expect(described_class.key_from_address("replies+key@example.com")).to eq("key")
end
end
+
+ context 'self.key_from_fallback_reply_message_id' do
+ it 'returns reply key' do
+ expect(described_class.key_from_fallback_reply_message_id('reply-key@localhost')).to eq('key')
+ end
+ end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 9b47acfe0cd..631b5094f42 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -35,7 +35,9 @@ describe Notify do
subject { Notify.new_issue_email(issue.assignee_id, issue.id) }
it_behaves_like 'an assignee email'
- it_behaves_like 'an email starting a new thread', 'issue'
+ it_behaves_like 'an email starting a new thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
@@ -73,9 +75,11 @@ describe Notify do
subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
- it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'an unsubscribeable thread'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -104,7 +108,9 @@ describe Notify do
subject { Notify.relabeled_issue_email(recipient.id, issue.id, %w[foo bar baz], current_user.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with a labels subscriptions link in its footer'
@@ -132,7 +138,9 @@ describe Notify do
let(:status) { 'closed' }
subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) }
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
@@ -163,7 +171,9 @@ describe Notify do
let(:new_issue) { create(:issue) }
subject { Notify.issue_moved_email(recipient, issue, new_issue, current_user) }
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
@@ -196,9 +206,11 @@ describe Notify do
subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
it_behaves_like 'an assignee email'
- it_behaves_like 'an email starting a new thread', 'merge_request'
+ it_behaves_like 'an email starting a new thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
- it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'an unsubscribeable thread'
it 'has the correct subject' do
is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
@@ -216,10 +228,6 @@ describe Notify do
is_expected.to have_body_text /#{merge_request.target_branch}/
end
- it 'has the correct message-id set' do
- is_expected.to have_header 'Message-ID', "<merge_request_#{merge_request.id}@#{Gitlab.config.gitlab.host}>"
- end
-
context 'when enabled email_author_in_body' do
before do
allow(current_application_settings).to receive(:email_author_in_body).and_return(true)
@@ -247,7 +255,9 @@ describe Notify do
subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like "an unsubscribeable thread"
@@ -278,7 +288,9 @@ describe Notify do
subject { Notify.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with a labels subscriptions link in its footer'
@@ -306,9 +318,11 @@ describe Notify do
let(:status) { 'reopened' }
subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) }
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
- it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'an unsubscribeable thread'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -337,9 +351,11 @@ describe Notify do
subject { Notify.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) }
it_behaves_like 'a multiple recipients email'
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
- it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'an unsubscribeable thread'
it 'is sent as the merge author' do
sender = subject.header[:from].addrs[0]
@@ -456,9 +472,11 @@ describe Notify do
subject { Notify.note_commit_email(recipient.id, note.id) }
it_behaves_like 'a note email'
- it_behaves_like 'an answer to an existing thread', 'commit'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { commit }
+ end
it_behaves_like 'it should show Gmail Actions View Commit link'
- it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'a user cannot unsubscribe through footer link'
it 'has the correct subject' do
is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/
@@ -477,7 +495,9 @@ describe Notify do
subject { Notify.note_merge_request_email(recipient.id, note.id) }
it_behaves_like 'a note email'
- it_behaves_like 'an answer to an existing thread', 'merge_request'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { merge_request }
+ end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
@@ -498,7 +518,9 @@ describe Notify do
subject { Notify.note_issue_email(recipient.id, note.id) }
it_behaves_like 'a note email'
- it_behaves_like 'an answer to an existing thread', 'issue'
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb
index 6019af544d3..56a6dbf96f9 100644
--- a/spec/mailers/shared/notify.rb
+++ b/spec/mailers/shared/notify.rb
@@ -10,6 +10,13 @@ shared_context 'gitlab email notification' do
ActionMailer::Base.deliveries.clear
email = recipient.emails.create(email: "notifications@example.com")
recipient.update_attribute(:notification_email, email.email)
+ stub_incoming_email_setting(enabled: true, address: "reply+%{key}@#{Gitlab.config.gitlab.host}")
+ end
+end
+
+shared_context 'reply-by-email is enabled with incoming address without %{key}' do
+ before do
+ stub_incoming_email_setting(enabled: true, address: "reply@#{Gitlab.config.gitlab.host}")
end
end
@@ -46,25 +53,76 @@ shared_examples 'an email with X-GitLab headers containing project details' do
end
end
-shared_examples 'an email starting a new thread' do |message_id_prefix|
- include_examples 'an email with X-GitLab headers containing project details'
+shared_examples 'a new thread email with reply-by-email enabled' do
+ let(:regex) { /\A<reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ }
+
+ it 'has a Message-ID header' do
+ is_expected.to have_header 'Message-ID', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>"
+ end
- it 'has a discussion identifier' do
- is_expected.to have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ it 'has a References header' do
+ is_expected.to have_header 'References', regex
end
end
-shared_examples 'an answer to an existing thread' do |thread_id_prefix|
+shared_examples 'a thread answer email with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details'
+ let(:regex) { /\A<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}> <reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ }
+
+ it 'has a Message-ID header' do
+ is_expected.to have_header 'Message-ID', /\A<(.*)@#{Gitlab.config.gitlab.host}>\Z/
+ end
+
+ it 'has a In-Reply-To header' do
+ is_expected.to have_header 'In-Reply-To', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>"
+ end
+
+ it 'has a References header' do
+ is_expected.to have_header 'References', regex
+ end
it 'has a subject that begins with Re: ' do
is_expected.to have_subject /^Re: /
end
+end
+
+shared_examples 'an email starting a new thread with reply-by-email enabled' do
+ include_examples 'an email with X-GitLab headers containing project details'
+ include_examples 'a new thread email with reply-by-email enabled'
+
+ context 'when reply-by-email is enabled with incoming address with %{key}' do
+ it 'has a Reply-To header' do
+ is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/
+ end
+ end
+
+ context 'when reply-by-email is enabled with incoming address without %{key}' do
+ include_context 'reply-by-email is enabled with incoming address without %{key}'
+ include_examples 'a new thread email with reply-by-email enabled'
+
+ it 'has a Reply-To header' do
+ is_expected.to have_header 'Reply-To', /<reply@#{Gitlab.config.gitlab.host}>\Z/
+ end
+ end
+end
+
+shared_examples 'an answer to an existing thread with reply-by-email enabled' do
+ include_examples 'an email with X-GitLab headers containing project details'
+ include_examples 'a thread answer email with reply-by-email enabled'
+
+ context 'when reply-by-email is enabled with incoming address with %{key}' do
+ it 'has a Reply-To header' do
+ is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/
+ end
+ end
+
+ context 'when reply-by-email is enabled with incoming address without %{key}' do
+ include_context 'reply-by-email is enabled with incoming address without %{key}'
+ include_examples 'a thread answer email with reply-by-email enabled'
- it 'has headers that reference an existing thread' do
- is_expected.to have_header 'Message-ID', /<(.*)@#{Gitlab.config.gitlab.host}>/
- is_expected.to have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
- is_expected.to have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
+ it 'has a Reply-To header' do
+ is_expected.to have_header 'Reply-To', /<reply@#{Gitlab.config.gitlab.host}>\Z/
+ end
end
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index b1764d7ac09..520cf1b75de 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -12,7 +12,6 @@
# updated_at :datetime
# home_page_url :string(255)
# default_branch_protection :integer default(2)
-# twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text
# version_check_enabled :boolean default(TRUE)
# max_attachment_size :integer default(10), not null
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index be29b6d66ff..b16ccc6e305 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -9,6 +9,7 @@ describe Issue, "Issuable" do
it { is_expected.to belong_to(:author) }
it { is_expected.to belong_to(:assignee) }
it { is_expected.to have_many(:notes).dependent(:destroy) }
+ it { is_expected.to have_many(:todos).dependent(:destroy) }
end
describe "Validation" do
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index fd1513cab1b..56a9fbe9720 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -20,24 +20,27 @@ require "spec_helper"
describe SystemHook, models: true do
describe "execute" do
- before(:each) do
- @system_hook = create(:system_hook)
- WebMock.stub_request(:post, @system_hook.url)
+ let(:system_hook) { create(:system_hook) }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+ let(:group) { create(:group) }
+
+ before do
+ WebMock.stub_request(:post, system_hook.url)
end
it "project_create hook" do
- Projects::CreateService.new(create(:user), name: 'empty').execute
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+ Projects::CreateService.new(user, name: 'empty').execute
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /project_create/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it "project_destroy hook" do
- user = create(:user)
- project = create(:empty_project, namespace: user.namespace)
Projects::DestroyService.new(project, user, {}).pending_delete!
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /project_destroy/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
@@ -45,37 +48,36 @@ describe SystemHook, models: true do
it "user_create hook" do
create(:user)
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_create/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it "user_destroy hook" do
- user = create(:user)
user.destroy
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_destroy/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it "project_create hook" do
- user = create(:user)
- project = create(:project)
project.team << [user, :master]
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_team/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it "project_destroy hook" do
- user = create(:user)
- project = create(:project)
project.team << [user, :master]
project.project_members.destroy_all
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_team/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
@@ -83,41 +85,39 @@ describe SystemHook, models: true do
it 'group create hook' do
create(:group)
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /group_create/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it 'group destroy hook' do
- group = create(:group)
group.destroy
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /group_destroy/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it 'group member create hook' do
- group = create(:group)
- user = create(:user)
group.add_master(user)
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_group/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
it 'group member destroy hook' do
- group = create(:group)
- user = create(:user)
group.add_master(user)
group.group_members.destroy_all
- expect(WebMock).to have_requested(:post, @system_hook.url).with(
+
+ expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_group/,
headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
).once
end
-
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index bd0a4ebe337..6f5d912fe5d 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -224,22 +224,22 @@ describe MergeRequest, models: true do
['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
it "detects the '#{wip_prefix}' prefix" do
subject.title = "#{wip_prefix}#{subject.title}"
- expect(subject).to be_work_in_progress
+ expect(subject.work_in_progress?).to eq true
end
end
it "doesn't detect WIP for words starting with WIP" do
subject.title = "Wipwap #{subject.title}"
- expect(subject).not_to be_work_in_progress
+ expect(subject.work_in_progress?).to eq false
end
it "doesn't detect WIP for words containing with WIP" do
subject.title = "WupWipwap #{subject.title}"
- expect(subject).not_to be_work_in_progress
+ expect(subject.work_in_progress?).to eq false
end
it "doesn't detect WIP by default" do
- expect(subject).not_to be_work_in_progress
+ expect(subject.work_in_progress?).to eq false
end
end
diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb
index 97e6f03e308..f648cbe2dee 100644
--- a/spec/models/project_services/slack_service/issue_message_spec.rb
+++ b/spec/models/project_services/slack_service/issue_message_spec.rb
@@ -27,6 +27,16 @@ describe SlackService::IssueMessage, models: true do
let(:color) { '#345' }
+ context '#initialize' do
+ before do
+ args[:object_attributes][:description] = nil
+ end
+
+ it 'returns a non-null description' do
+ expect(subject.description).to eq('')
+ end
+ end
+
context 'open' do
it 'returns a message regarding opening of issues' do
expect(subject.pretext).to eq(
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 55f1c665b86..f29c389e094 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -104,6 +104,15 @@ describe Project, models: true do
end
end
+ describe 'default_scope' do
+ it 'excludes projects pending deletion from the results' do
+ project = create(:empty_project)
+ create(:empty_project, pending_delete: true)
+
+ expect(Project.all).to eq [project]
+ end
+ end
+
describe 'project token' do
it 'should set an random token if none provided' do
project = FactoryGirl.create :empty_project, runners_token: ''
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index f10d671104c..f517f325c03 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -303,7 +303,7 @@ describe Repository, models: true do
describe 'when there are no branches' do
before do
- allow(repository.raw_repository).to receive(:branch_count).and_return(0)
+ allow(repository).to receive(:branch_count).and_return(0)
end
it { is_expected.to eq(false) }
@@ -311,13 +311,13 @@ describe Repository, models: true do
describe 'when there are branches' do
it 'returns true' do
- expect(repository.raw_repository).to receive(:branch_count).and_return(3)
+ expect(repository).to receive(:branch_count).and_return(3)
expect(subject).to eq(true)
end
it 'caches the output' do
- expect(repository.raw_repository).to receive(:branch_count).
+ expect(repository).to receive(:branch_count).
once.
and_return(3)
@@ -436,7 +436,7 @@ describe Repository, models: true do
it 'expires the visible content cache' do
repository.has_visible_content?
- expect(repository.raw_repository).to receive(:branch_count).
+ expect(repository).to receive(:branch_count).
once.
and_return(0)
@@ -558,7 +558,7 @@ describe Repository, models: true do
end
it 'flushes the exists cache' do
- expect(repository).to receive(:expire_exists_cache)
+ expect(repository).to receive(:expire_exists_cache).twice
repository.before_delete
end
@@ -612,6 +612,20 @@ describe Repository, models: true do
end
end
+ describe '#before_import' do
+ it 'flushes the emptiness cachess' do
+ expect(repository).to receive(:expire_emptiness_caches)
+
+ repository.before_import
+ end
+
+ it 'flushes the exists cache' do
+ expect(repository).to receive(:expire_exists_cache)
+
+ repository.before_import
+ end
+ end
+
describe '#after_import' do
it 'flushes the emptiness cachess' do
expect(repository).to receive(:expire_emptiness_caches)
@@ -865,4 +879,21 @@ describe Repository, models: true do
repository.build_cache
end
end
+
+ describe '#local_branches' do
+ it 'returns the local branches' do
+ masterrev = repository.find_branch('master').target
+ create_remote_branch('joe', 'remote_branch', masterrev)
+ repository.add_branch(user, 'local_branch', masterrev)
+
+ expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false)
+ expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true)
+ end
+ end
+
+ def create_remote_branch(remote_name, branch_name, target)
+ rugged = repository.rugged
+ rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target)
+ end
+
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 0ab7fd88ce6..8b2fb77e28e 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -173,6 +173,13 @@ describe User, models: true do
expect(user).to be_invalid
end
end
+
+ context 'owns_notification_email' do
+ it 'accepts temp_oauth_email emails' do
+ user = build(:user, email: "temp-email-for-oauth@example.com")
+ expect(user).to be_valid
+ end
+ end
end
end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 667f0dbea5c..6943ff9d26c 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -23,13 +23,25 @@ describe API::API, api: true do
end
describe 'POST /projects/:id/labels' do
- it 'should return created label' do
+ it 'should return created label when all params' do
+ post api("/projects/#{project.id}/labels", user),
+ name: 'Foo',
+ color: '#FFAABB',
+ description: 'test'
+ expect(response.status).to eq(201)
+ expect(json_response['name']).to eq('Foo')
+ expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['description']).to eq('test')
+ end
+
+ it 'should return created label when only required params' do
post api("/projects/#{project.id}/labels", user),
name: 'Foo',
color: '#FFAABB'
expect(response.status).to eq(201)
expect(json_response['name']).to eq('Foo')
expect(json_response['color']).to eq('#FFAABB')
+ expect(json_response['description']).to be_nil
end
it 'should return a 400 bad request if name not given' do
@@ -94,14 +106,16 @@ describe API::API, api: true do
end
describe 'PUT /projects/:id/labels' do
- it 'should return 200 if name and colors are changed' do
+ it 'should return 200 if name and colors and description are changed' do
put api("/projects/#{project.id}/labels", user),
name: 'label1',
new_name: 'New Label',
- color: '#FFFFFF'
+ color: '#FFFFFF',
+ description: 'test'
expect(response.status).to eq(200)
expect(json_response['name']).to eq('New Label')
expect(json_response['color']).to eq('#FFFFFF')
+ expect(json_response['description']).to eq('test')
end
it 'should return 200 if name is changed' do
@@ -122,6 +136,15 @@ describe API::API, api: true do
expect(json_response['color']).to eq('#FFFFFF')
end
+ it 'should return 200 if description is changed' do
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
+ description: 'test'
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq(label1.name)
+ expect(json_response['description']).to eq('test')
+ end
+
it 'should return 404 if label does not exist' do
put api("/projects/#{project.id}/labels", user),
name: 'label2',
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index c9175a4d6eb..25fa30b2f21 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -118,6 +118,7 @@ describe API::API, api: true do
expect(response.status).to eq(200)
expect(json_response['title']).to eq(merge_request.title)
expect(json_response['iid']).to eq(merge_request.iid)
+ expect(json_response['work_in_progress']).to eq(false)
expect(json_response['merge_status']).to eq('can_be_merged')
end
@@ -133,6 +134,16 @@ describe API::API, api: true do
get api("/projects/#{project.id}/merge_requests/999", user)
expect(response.status).to eq(404)
end
+
+ context 'Work in Progress' do
+ let!(:merge_request_wip) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "WIP: Test", created_at: base_time + 1.second) }
+
+ it "should return merge_request" do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.id}", user)
+ expect(response.status).to eq(200)
+ expect(json_response['work_in_progress']).to eq(true)
+ end
+ end
end
describe 'GET /projects/:id/merge_requests/:merge_request_id/commits' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index a5d4985dc78..be2034e0f39 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -948,6 +948,78 @@ describe API::API, api: true do
end
end
+ describe 'POST /projects/:id/archive' do
+ context 'on an unarchived project' do
+ it 'archives the project' do
+ post api("/projects/#{project.id}/archive", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response['archived']).to be_truthy
+ end
+ end
+
+ context 'on an archived project' do
+ before do
+ project.archive!
+ end
+
+ it 'remains archived' do
+ post api("/projects/#{project.id}/archive", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response['archived']).to be_truthy
+ end
+ end
+
+ context 'user without archiving rights to the project' do
+ before do
+ project.team << [user3, :developer]
+ end
+
+ it 'rejects the action' do
+ post api("/projects/#{project.id}/archive", user3)
+
+ expect(response.status).to eq(403)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/unarchive' do
+ context 'on an unarchived project' do
+ it 'remains unarchived' do
+ post api("/projects/#{project.id}/unarchive", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response['archived']).to be_falsey
+ end
+ end
+
+ context 'on an archived project' do
+ before do
+ project.archive!
+ end
+
+ it 'unarchives the project' do
+ post api("/projects/#{project.id}/unarchive", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response['archived']).to be_falsey
+ end
+ end
+
+ context 'user without archiving rights to the project' do
+ before do
+ project.team << [user3, :developer]
+ end
+
+ it 'rejects the action' do
+ post api("/projects/#{project.id}/unarchive", user3)
+
+ expect(response.status).to eq(403)
+ end
+ end
+ end
+
describe 'DELETE /projects/:id' do
context 'when authenticated as user' do
it 'should remove project' do
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index 9b0c73aaf37..2a5e4ac3ec4 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -160,6 +160,20 @@ describe Issues::MoveService, services: true do
.to eq "Note with reference to merge request #{old_project.to_reference}!1"
end
end
+
+ context 'issue description with uploads' do
+ let(:uploader) { build(:file_uploader, project: old_project) }
+ let(:description) { "Text and #{uploader.to_markdown}" }
+
+ include_context 'issue move executed'
+
+ it 'rewrites uploads in description' do
+ expect(new_issue.description).to_not eq description
+ expect(new_issue.description)
+ .to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/)
+ expect(new_issue.description).to_not include uploader.secret
+ end
+ end
end
describe 'rewritting references' do
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index 04f474c736c..32bf3acf483 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -72,6 +72,23 @@ describe Projects::ImportService, services: true do
expect(result[:status]).to eq :success
end
+ it 'flushes various caches' do
+ expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).
+ with(project.path_with_namespace, project.import_url).
+ and_return(true)
+
+ expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).
+ and_return(true)
+
+ expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
+ and_call_original
+
+ expect_any_instance_of(Repository).to receive(:expire_exists_cache).
+ and_call_original
+
+ subject.execute
+ end
+
it 'fails if importer fails' do
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(false)
diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb
new file mode 100644
index 00000000000..23f5555d3e0
--- /dev/null
+++ b/spec/services/projects/unlink_fork_service_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe Projects::UnlinkForkService, services: true do
+ subject { Projects::UnlinkForkService.new(fork_project, user) }
+
+ let(:fork_link) { create(:forked_project_link) }
+ let(:fork_project) { fork_link.forked_to_project }
+ let(:user) { create(:user) }
+
+ context 'with opened merge request on the source project' do
+ let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: fork_link.forked_from_project) }
+ let(:mr_close_service) { MergeRequests::CloseService.new(fork_project, user) }
+
+ before do
+ allow(MergeRequests::CloseService).to receive(:new).
+ with(fork_project, user).
+ and_return(mr_close_service)
+ end
+
+ it 'close all pending merge requests' do
+ expect(mr_close_service).to receive(:execute).with(merge_request)
+
+ subject.execute
+ end
+ end
+
+ it 'remove fork relation' do
+ expect(fork_project.forked_project_link).to receive(:destroy)
+
+ subject.execute
+ end
+end
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index b4728807b8b..82b7fbfa816 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -2,22 +2,25 @@ require 'spec_helper'
describe TodoService, services: true do
let(:author) { create(:user) }
- let(:john_doe) { create(:user, username: 'john_doe') }
- let(:michael) { create(:user, username: 'michael') }
- let(:stranger) { create(:user, username: 'stranger') }
+ let(:assignee) { create(:user) }
+ let(:non_member) { create(:user) }
+ let(:member) { create(:user) }
+ let(:admin) { create(:admin) }
+ let(:john_doe) { create(:user) }
let(:project) { create(:project) }
- let(:mentions) { [author.to_reference, john_doe.to_reference, michael.to_reference, stranger.to_reference].join(' ') }
+ let(:mentions) { [author, assignee, john_doe, member, non_member, admin].map(&:to_reference).join(' ') }
let(:service) { described_class.new }
before do
project.team << [author, :developer]
+ project.team << [member, :developer]
project.team << [john_doe, :developer]
- project.team << [michael, :developer]
end
describe 'Issues' do
let(:issue) { create(:issue, project: project, assignee: john_doe, author: author, description: mentions) }
let(:unassigned_issue) { create(:issue, project: project, assignee: nil) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee, description: mentions) }
describe '#new_issue' do
it 'creates a todo if assigned' do
@@ -37,10 +40,20 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.new_issue(issue, author)
- should_create_todo(user: michael, target: issue, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
- should_not_create_todo(user: stranger, target: issue, action: Todo::MENTIONED)
+ should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
+ end
+
+ it 'does not create todo for non project members when issue is confidential' do
+ service.new_issue(confidential_issue, john_doe)
+
+ should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED)
+ should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end
end
@@ -48,16 +61,26 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.update_issue(issue, author)
- should_create_todo(user: michael, target: issue, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: issue, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
- should_not_create_todo(user: stranger, target: issue, action: Todo::MENTIONED)
+ should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end
it 'does not create a todo if user was already mentioned' do
- create(:todo, :mentioned, user: michael, project: project, target: issue, author: author)
+ create(:todo, :mentioned, user: member, project: project, target: issue, author: author)
- expect { service.update_issue(issue, author) }.not_to change(michael.todos, :count)
+ expect { service.update_issue(issue, author) }.not_to change(member.todos, :count)
+ end
+
+ it 'does not create todo for non project members when issue is confidential' do
+ service.update_issue(confidential_issue, john_doe)
+
+ should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
+ should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end
end
@@ -109,8 +132,10 @@ describe TodoService, services: true do
describe '#new_note' do
let!(:first_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
let!(:second_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
let(:note) { create(:note, project: project, noteable: issue, author: john_doe, note: mentions) }
let(:note_on_commit) { create(:note_on_commit, project: project, author: john_doe, note: mentions) }
+ let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project, note: mentions) }
let(:note_on_project_snippet) { create(:note_on_project_snippet, project: project, author: john_doe, note: mentions) }
let(:award_note) { create(:note, :award, project: project, noteable: issue, author: john_doe, note: 'thumbsup') }
let(:system_note) { create(:system_note, project: project, noteable: issue) }
@@ -142,19 +167,29 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.new_note(note, john_doe)
- should_create_todo(user: michael, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
- should_not_create_todo(user: stranger, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ end
+
+ it 'does not create todo for non project members when leaving a note on a confidential issue' do
+ service.new_note(note_on_confidential_issue, john_doe)
+
+ should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
+ should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
end
it 'creates a todo for each valid mentioned user when leaving a note on commit' do
service.new_note(note_on_commit, john_doe)
- should_create_todo(user: michael, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
+ should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
should_not_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
- should_not_create_todo(user: stranger, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
+ should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
end
it 'does not create todo when leaving a note on snippet' do
@@ -185,10 +220,10 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.new_merge_request(mr_assigned, author)
- should_create_todo(user: michael, target: mr_assigned, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
- should_not_create_todo(user: stranger, target: mr_assigned, action: Todo::MENTIONED)
+ should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end
end
@@ -196,16 +231,16 @@ describe TodoService, services: true do
it 'creates a todo for each valid mentioned user' do
service.update_merge_request(mr_assigned, author)
- should_create_todo(user: michael, target: mr_assigned, action: Todo::MENTIONED)
+ should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
- should_not_create_todo(user: stranger, target: mr_assigned, action: Todo::MENTIONED)
+ should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end
it 'does not create a todo if user was already mentioned' do
- create(:todo, :mentioned, user: michael, project: project, target: mr_assigned, author: author)
+ create(:todo, :mentioned, user: member, project: project, target: mr_assigned, author: author)
- expect { service.update_merge_request(mr_assigned, author) }.not_to change(michael.todos, :count)
+ expect { service.update_merge_request(mr_assigned, author) }.not_to change(member.todos, :count)
end
end
diff --git a/spec/support/carrierwave.rb b/spec/support/carrierwave.rb
new file mode 100644
index 00000000000..aa89afd8fb3
--- /dev/null
+++ b/spec/support/carrierwave.rb
@@ -0,0 +1,7 @@
+CarrierWave.root = 'tmp/tests/uploads'
+
+RSpec.configure do |config|
+ config.after(:suite) do
+ FileUtils.rm_rf('tmp/tests/uploads')
+ end
+end
diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb
index ef5ea7d626e..e849a9633b9 100644
--- a/spec/support/filter_spec_helper.rb
+++ b/spec/support/filter_spec_helper.rb
@@ -78,6 +78,6 @@ module FilterSpecHelper
# Shortcut to Rails' auto-generated routes helpers, to avoid including the
# module
def urls
- Gitlab::Application.routes.url_helpers
+ Gitlab::Routing.url_helpers
end
end
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index 73c6792b65f..b87cd6bbca2 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -106,7 +106,7 @@ class MarkdownFeature
end
def urls
- Gitlab::Application.routes.url_helpers
+ Gitlab::Routing.url_helpers
end
def raw_markdown
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 0d1bd030f3c..71664bb192e 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -15,6 +15,7 @@ module TestEnv
'lfs' => 'be93687',
'master' => '5937ac0',
"'test'" => 'e56497b',
+ 'orphaned-branch' => '45127a9',
}
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 320be9a0b61..05fc4c4554f 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -7,8 +7,12 @@ describe 'gitlab:app namespace rake task' do
Rake.application.rake_require 'tasks/gitlab/backup'
Rake.application.rake_require 'tasks/gitlab/shell'
Rake.application.rake_require 'tasks/gitlab/db'
+
# empty task as env is already loaded
Rake::Task.define_task :environment
+
+ # We need this directory to run `gitlab:backup:create` task
+ FileUtils.mkdir_p('public/uploads')
end
def run_rake_task(task_name)
diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb
index b11c5de94e3..1abd87d7d33 100644
--- a/spec/workers/merge_worker_spec.rb
+++ b/spec/workers/merge_worker_spec.rb
@@ -22,6 +22,8 @@ describe MergeWorker do
merge_request.reload
expect(merge_request).to be_merged
+
+ source_project.repository.expire_branches_cache
expect(source_project.repository.branch_names).not_to include('markdown')
end
end
diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb
new file mode 100644
index 00000000000..7e59bd2fced
--- /dev/null
+++ b/spec/workers/project_cache_worker_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe ProjectCacheWorker do
+ let(:project) { create(:project) }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ it 'updates project cache data' do
+
+ expect_any_instance_of(Repository).to receive(:size)
+ expect_any_instance_of(Repository).to receive(:commit_count)
+
+ expect_any_instance_of(Project).to receive(:update_repository_size)
+ expect_any_instance_of(Project).to receive(:update_commit_count)
+
+ subject.perform(project.id)
+ end
+
+ it 'handles missing repository data' do
+ expect_any_instance_of(Repository).to receive(:exists?).and_return(false)
+ expect_any_instance_of(Repository).not_to receive(:size)
+
+ subject.perform(project.id)
+ end
+ end
+end