From e0dc73527a478188cfa28b456b64798639aa73c9 Mon Sep 17 00:00:00 2001 From: Jarka Kadlecova Date: Fri, 24 Mar 2017 11:11:36 +0100 Subject: Project deploy keys json end point --- app/controllers/projects/deploy_keys_controller.rb | 9 ++- .../projects/settings/deploy_keys_presenter.rb | 11 ++++ app/serializers/deploy_key_entity.rb | 12 ++++ app/serializers/deploy_key_serializer.rb | 3 + app/serializers/project_entity.rb | 12 ++++ changelogs/unreleased/29667-deploy-keys.yml | 4 ++ .../projects/deploy_keys_controller_spec.rb | 66 ++++++++++++++++++++++ spec/serializers/deploy_key_entity_spec.rb | 29 ++++++++++ 8 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 app/serializers/deploy_key_entity.rb create mode 100644 app/serializers/deploy_key_serializer.rb create mode 100644 app/serializers/project_entity.rb create mode 100644 changelogs/unreleased/29667-deploy-keys.yml create mode 100644 spec/controllers/projects/deploy_keys_controller_spec.rb create mode 100644 spec/serializers/deploy_key_entity_spec.rb diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index d0c44e297e3..a47e15a192b 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -8,7 +8,12 @@ class Projects::DeployKeysController < Projects::ApplicationController layout "project_settings" def index - redirect_to_repository_settings(@project) + respond_to do |format| + format.html { redirect_to_repository_settings(@project) } + format.json do + render json: Projects::Settings::DeployKeysPresenter.new(@project, current_user: current_user).as_json + end + end end def new @@ -19,7 +24,7 @@ class Projects::DeployKeysController < Projects::ApplicationController @key = DeployKey.new(deploy_key_params.merge(user: current_user)) unless @key.valid? && @project.deploy_keys << @key - flash[:alert] = @key.errors.full_messages.join(', ').html_safe + flash[:alert] = @key.errors.full_messages.join(', ').html_safe end redirect_to_repository_settings(@project) end diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb index 86ac513b3c0..070b0c35e36 100644 --- a/app/presenters/projects/settings/deploy_keys_presenter.rb +++ b/app/presenters/projects/settings/deploy_keys_presenter.rb @@ -48,6 +48,17 @@ module Projects available_public_keys.any? end + def as_json + serializer = DeployKeySerializer.new + opts = { user: current_user } + + { + enabled_keys: serializer.represent(enabled_keys, opts), + available_project_keys: serializer.represent(available_project_keys, opts), + public_keys: serializer.represent(available_public_keys, opts) + } + end + def to_partial_path 'projects/deploy_keys/index' end diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb new file mode 100644 index 00000000000..cdedc2c7bd0 --- /dev/null +++ b/app/serializers/deploy_key_entity.rb @@ -0,0 +1,12 @@ +class DeployKeyEntity < Grape::Entity + expose :id + expose :user_id + expose :title + expose :fingerprint + expose :can_push + expose :created_at + expose :updated_at + expose :projects, using: ProjectEntity do |deploy_key| + deploy_key.projects.select { |project| options[:user].can?(:read_project, project) } + end +end diff --git a/app/serializers/deploy_key_serializer.rb b/app/serializers/deploy_key_serializer.rb new file mode 100644 index 00000000000..8f849eb88b7 --- /dev/null +++ b/app/serializers/deploy_key_serializer.rb @@ -0,0 +1,3 @@ +class DeployKeySerializer < BaseSerializer + entity DeployKeyEntity +end diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb new file mode 100644 index 00000000000..6f8061f7530 --- /dev/null +++ b/app/serializers/project_entity.rb @@ -0,0 +1,12 @@ +class ProjectEntity < Grape::Entity + expose :id + expose :name + + expose :full_path do |project| + project.full_path + end + + expose :full_name do |project| + project.full_name + end +end diff --git a/changelogs/unreleased/29667-deploy-keys.yml b/changelogs/unreleased/29667-deploy-keys.yml new file mode 100644 index 00000000000..0f202ebf1ee --- /dev/null +++ b/changelogs/unreleased/29667-deploy-keys.yml @@ -0,0 +1,4 @@ +--- +title: Project deploy keys json end point +merge_request: +author: diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb new file mode 100644 index 00000000000..efe1a78415b --- /dev/null +++ b/spec/controllers/projects/deploy_keys_controller_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Projects::DeployKeysController do + let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + + sign_in(user) + end + + describe 'GET index' do + let(:params) do + { namespace_id: project.namespace, project_id: project } + end + + context 'when html requested' do + it 'redirects to blob' do + get :index, params + + expect(response).to redirect_to(namespace_project_settings_repository_path(params)) + end + end + + context 'when json requested' do + let(:project2) { create(:empty_project, :internal)} + let(:project_private) { create(:empty_project, :private)} + + let(:deploy_key_internal) do + create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com') + end + let(:deploy_key_actual) do + create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com') + end + let!(:deploy_key_public) { create(:deploy_key, public: true) } + + let!(:deploy_keys_project_internal) do + create(:deploy_keys_project, project: project2, deploy_key: deploy_key_internal) + end + + let!(:deploy_keys_actual_project) do + create(:deploy_keys_project, project: project, deploy_key: deploy_key_actual) + end + + let!(:deploy_keys_project_private) do + create(:deploy_keys_project, project: project_private, deploy_key: create(:another_deploy_key)) + end + + before do + project2.team << [user, :developer] + end + + it 'returns json in a correct format' do + get :index, params.merge(format: :json) + + json = JSON.parse(response.body) + + expect(json.keys).to match_array(%w(enabled_keys available_project_keys public_keys)) + expect(json['enabled_keys'].count).to eq(1) + expect(json['available_project_keys'].count).to eq(1) + expect(json['public_keys'].count).to eq(1) + end + end + end +end diff --git a/spec/serializers/deploy_key_entity_spec.rb b/spec/serializers/deploy_key_entity_spec.rb new file mode 100644 index 00000000000..da1d33b42ac --- /dev/null +++ b/spec/serializers/deploy_key_entity_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe DeployKeyEntity do + let(:user) { create(:user) } + let(:project) { create(:empty_project, :internal)} + let(:project_private) { create(:empty_project, :private)} + let(:deploy_key) { create(:deploy_key) } + let!(:deploy_key_internal) { create(:deploy_keys_project, project: project, deploy_key: deploy_key) } + let!(:deploy_key_private) { create(:deploy_keys_project, project: project_private, deploy_key: deploy_key) } + + let(:entity) { described_class.new(deploy_key, user: user) } + + it 'returns deploy keys with projects a user can read' do + expected_result = { + id: deploy_key.id, + user_id: deploy_key.user_id, + title: deploy_key.title, + fingerprint: deploy_key.fingerprint, + can_push: deploy_key.can_push, + created_at: deploy_key.created_at, + updated_at: deploy_key.updated_at, + projects: [ + { id: project.id, name: project.name, full_path: project.full_path, full_name: project.full_name } + ] + } + + expect(entity.as_json).to eq(expected_result) + end +end -- cgit v1.2.1 From 601f50c6421f135b52f737a3b59baa32e6a8f1fd Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 27 Mar 2017 11:22:43 +0200 Subject: Add endpoint that returns a list of deployments that happened within last 8.hours add index created_at --- app/controllers/projects/deployments_controller.rb | 16 +++++++ config/routes/project.rb | 2 + ...27091750_add_created_at_index_to_deployments.rb | 11 +++++ db/schema.rb | 3 +- .../projects/deployments_controller_spec.rb | 50 ++++++++++++++++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 app/controllers/projects/deployments_controller.rb create mode 100644 db/migrate/20170327091750_add_created_at_index_to_deployments.rb create mode 100644 spec/controllers/projects/deployments_controller_spec.rb diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb new file mode 100644 index 00000000000..4606ddd62cc --- /dev/null +++ b/app/controllers/projects/deployments_controller.rb @@ -0,0 +1,16 @@ +class Projects::DeploymentsController < Projects::ApplicationController + before_action :authorize_read_deployment! + + def index + deployments = environment.deployments.where('created_at > ?', 8.hours.ago) + .map { |d| d.slice(:id, :iid, :created_at, :sha, :ref, :tag) } + + render json: { deployments: deployments } + end + + private + + def environment + @environment ||= project.environments.find(params[:environment_id]) + end +end diff --git a/config/routes/project.rb b/config/routes/project.rb index 823e0614aeb..39144b74629 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -168,6 +168,8 @@ constraints(ProjectUrlConstrainer.new) do collection do get :folder, path: 'folders/:id' end + + resources :deployments, only: [:index] end resource :cycle_analytics, only: [:show] diff --git a/db/migrate/20170327091750_add_created_at_index_to_deployments.rb b/db/migrate/20170327091750_add_created_at_index_to_deployments.rb new file mode 100644 index 00000000000..cc615208c41 --- /dev/null +++ b/db/migrate/20170327091750_add_created_at_index_to_deployments.rb @@ -0,0 +1,11 @@ +class AddCreatedAtIndexToDeployments < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def change + add_concurrent_index :deployments, :created_at + end +end diff --git a/db/schema.rb b/db/schema.rb index f476637ceb2..fe4c5c3f356 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: 20170317203554) do +ActiveRecord::Schema.define(version: 20170327091750) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -346,6 +346,7 @@ ActiveRecord::Schema.define(version: 20170317203554) do t.string "on_stop" end + add_index "deployments", ["created_at"], name: "index_deployments_on_created_at", using: :btree add_index "deployments", ["project_id", "environment_id", "iid"], name: "index_deployments_on_project_id_and_environment_id_and_iid", using: :btree add_index "deployments", ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb new file mode 100644 index 00000000000..97a9fb8ae61 --- /dev/null +++ b/spec/controllers/projects/deployments_controller_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Projects::DeploymentsController do + include ApiHelpers + + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + let(:environment) { create(:environment, name: 'production', project: project) } + let(:deployment) { create(:deployment, project: project, environment: environment) } + + before do + project.team << [user, :master] + + sign_in(user) + end + + describe 'GET #index' do + it 'returns list of deployments withing last 8 hours' do + create(:deployment, environment: environment, created_at: 9.hours.ago) + create(:deployment, environment: environment, created_at: 7.hours.ago) + create(:deployment, environment: environment) + + get :index, environment_params + + expect(response).to be_ok + + expect(json_response['deployments'].count).to eq(2) + end + + it 'returns a list with deployments information' do + deployment = create(:deployment, environment: environment) + + get :index, environment_params + expect(response).to be_ok + + deployments = json_response['deployments'] + deployment_info = deployments.first.with_indifferent_access + created_at = deployment_info.delete(:created_at).to_time.utc + + expect(deployments.count).to eq(1) + expect(deployment_info).to include(:id, :iid, :sha, :ref, :tag) + expect(deployment).to have_attributes(deployment_info) + expect(deployment.created_at).to be_within(1.second).of(created_at) + end + end + + def environment_params(opts={}) + opts.reverse_merge(namespace_id: project.namespace, project_id: project, environment_id: environment.id) + end +end -- cgit v1.2.1 From 73203873438d666427228ffdbfe835ea4ffddaa6 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 28 Mar 2017 12:48:53 +0200 Subject: Use DeploymentSerializer to create deployment json --- app/controllers/projects/deployments_controller.rb | 3 ++- app/serializers/deployment_entity.rb | 1 + app/serializers/deployment_serializer.rb | 3 +++ spec/controllers/projects/deployments_controller_spec.rb | 7 ++----- spec/serializers/deployment_entity_spec.rb | 4 ++++ 5 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 app/serializers/deployment_serializer.rb diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb index 4606ddd62cc..ed0f802dbd9 100644 --- a/app/controllers/projects/deployments_controller.rb +++ b/app/controllers/projects/deployments_controller.rb @@ -2,8 +2,9 @@ class Projects::DeploymentsController < Projects::ApplicationController before_action :authorize_read_deployment! def index + serializer = DeploymentSerializer.new(user: @current_user) deployments = environment.deployments.where('created_at > ?', 8.hours.ago) - .map { |d| d.slice(:id, :iid, :created_at, :sha, :ref, :tag) } + .map { |d| serializer.represent(d) } render json: { deployments: deployments } end diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index d610fbe0c8a..d9ade5d87e2 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -18,6 +18,7 @@ class DeploymentEntity < Grape::Entity end end + expose :created_at expose :tag expose :last? expose :user, using: UserEntity diff --git a/app/serializers/deployment_serializer.rb b/app/serializers/deployment_serializer.rb new file mode 100644 index 00000000000..4b4bf6cd526 --- /dev/null +++ b/app/serializers/deployment_serializer.rb @@ -0,0 +1,3 @@ +class DeploymentSerializer < BaseSerializer + entity DeploymentEntity +end diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb index 97a9fb8ae61..b0d543177cc 100644 --- a/spec/controllers/projects/deployments_controller_spec.rb +++ b/spec/controllers/projects/deployments_controller_spec.rb @@ -28,19 +28,16 @@ describe Projects::DeploymentsController do end it 'returns a list with deployments information' do - deployment = create(:deployment, environment: environment) + create(:deployment, environment: environment) get :index, environment_params expect(response).to be_ok deployments = json_response['deployments'] deployment_info = deployments.first.with_indifferent_access - created_at = deployment_info.delete(:created_at).to_time.utc expect(deployments.count).to eq(1) - expect(deployment_info).to include(:id, :iid, :sha, :ref, :tag) - expect(deployment).to have_attributes(deployment_info) - expect(deployment.created_at).to be_within(1.second).of(created_at) + expect(deployment_info).to include(:id, :iid, :sha, :ref, :tag, :created_at) end end diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb index 95eca5463eb..73bd60b62e5 100644 --- a/spec/serializers/deployment_entity_spec.rb +++ b/spec/serializers/deployment_entity_spec.rb @@ -24,4 +24,8 @@ describe DeploymentEntity do expect(subject[:ref][:name]).to eq 'master' expect(subject[:ref][:ref_path]).not_to be_empty end + + it 'exposes creation date' do + expect(subject).to include(:created_at) + end end -- cgit v1.2.1 From d3e794e26f806fe491269a74e76c39669433c0fe Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 28 Mar 2017 14:56:30 +0200 Subject: Optionally filter by deployment time --- app/controllers/projects/deployments_controller.rb | 8 +++++--- spec/controllers/projects/deployments_controller_spec.rb | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb index ed0f802dbd9..a8a6f09d9df 100644 --- a/app/controllers/projects/deployments_controller.rb +++ b/app/controllers/projects/deployments_controller.rb @@ -2,9 +2,11 @@ class Projects::DeploymentsController < Projects::ApplicationController before_action :authorize_read_deployment! def index - serializer = DeploymentSerializer.new(user: @current_user) - deployments = environment.deployments.where('created_at > ?', 8.hours.ago) - .map { |d| serializer.represent(d) } + serializer = DeploymentSerializer.new(user: @current_user, project: project) + + deployments = environment.deployments.reorder(created_at: :desc) + deployments = deployments.where('created_at > ?', params[:after].to_time) if params[:after]&.to_time + deployments = deployments.map { |deployment| serializer.represent(deployment) } render json: { deployments: deployments } end diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb index b0d543177cc..7b7f16e14da 100644 --- a/spec/controllers/projects/deployments_controller_spec.rb +++ b/spec/controllers/projects/deployments_controller_spec.rb @@ -20,7 +20,7 @@ describe Projects::DeploymentsController do create(:deployment, environment: environment, created_at: 7.hours.ago) create(:deployment, environment: environment) - get :index, environment_params + get :index, environment_params(:after => 8.hours.ago) expect(response).to be_ok -- cgit v1.2.1 From 5e2219db48719af5ca971f9222fffa7bd66cb6d8 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 28 Mar 2017 15:54:02 +0200 Subject: Fix unreversible migration, and small rubocop warnings --- db/migrate/20170327091750_add_created_at_index_to_deployments.rb | 6 +++++- spec/controllers/projects/deployments_controller_spec.rb | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/db/migrate/20170327091750_add_created_at_index_to_deployments.rb b/db/migrate/20170327091750_add_created_at_index_to_deployments.rb index cc615208c41..9cc35d08e1a 100644 --- a/db/migrate/20170327091750_add_created_at_index_to_deployments.rb +++ b/db/migrate/20170327091750_add_created_at_index_to_deployments.rb @@ -5,7 +5,11 @@ class AddCreatedAtIndexToDeployments < ActiveRecord::Migration disable_ddl_transaction! - def change + def up add_concurrent_index :deployments, :created_at end + + def down + remove_index :deployments, :created_at + end end diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb index 7b7f16e14da..fbe510fb539 100644 --- a/spec/controllers/projects/deployments_controller_spec.rb +++ b/spec/controllers/projects/deployments_controller_spec.rb @@ -20,7 +20,7 @@ describe Projects::DeploymentsController do create(:deployment, environment: environment, created_at: 7.hours.ago) create(:deployment, environment: environment) - get :index, environment_params(:after => 8.hours.ago) + get :index, environment_params(after: 8.hours.ago) expect(response).to be_ok @@ -41,7 +41,7 @@ describe Projects::DeploymentsController do end end - def environment_params(opts={}) + def environment_params(opts = {}) opts.reverse_merge(namespace_id: project.namespace, project_id: project, environment_id: environment.id) end end -- cgit v1.2.1 From 3bfe3febef2903e27b06e319cbf259546cea7841 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Thu, 6 Apr 2017 21:47:14 +0200 Subject: Make MR link in build sidebar bold It adds some consistency compared to the links in the header, which are also bold. --- app/views/projects/builds/_sidebar.html.haml | 3 ++- changelogs/unreleased/tc-job-page-mr-bold.yml | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/tc-job-page-mr-bold.yml diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index f4a66398c85..694f524faed 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -48,7 +48,8 @@ - if @build.merge_request %p.build-detail-row %span.build-light-text Merge Request: - = link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request) + = link_to merge_request_path(@build.merge_request) do + %strong= "#{@build.merge_request.to_reference}" - if @build.duration %p.build-detail-row %span.build-light-text Duration: diff --git a/changelogs/unreleased/tc-job-page-mr-bold.yml b/changelogs/unreleased/tc-job-page-mr-bold.yml new file mode 100644 index 00000000000..0243a259119 --- /dev/null +++ b/changelogs/unreleased/tc-job-page-mr-bold.yml @@ -0,0 +1,4 @@ +--- +title: Make MR link in build sidebar bold +merge_request: +author: -- cgit v1.2.1 From 703df2881bb137a79284baafe2cc12ff32ab9ff5 Mon Sep 17 00:00:00 2001 From: Jarka Kadlecova Date: Fri, 7 Apr 2017 13:34:39 +0200 Subject: expose additional values in deploy key entity --- app/serializers/deploy_key_entity.rb | 2 ++ spec/serializers/deploy_key_entity_spec.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb index cdedc2c7bd0..d75a83d0fa5 100644 --- a/app/serializers/deploy_key_entity.rb +++ b/app/serializers/deploy_key_entity.rb @@ -4,6 +4,8 @@ class DeployKeyEntity < Grape::Entity expose :title expose :fingerprint expose :can_push + expose :destroyed_when_orphaned?, as: :destroyed_when_orphaned + expose :almost_orphaned?, as: :almost_orphaned expose :created_at expose :updated_at expose :projects, using: ProjectEntity do |deploy_key| diff --git a/spec/serializers/deploy_key_entity_spec.rb b/spec/serializers/deploy_key_entity_spec.rb index da1d33b42ac..cc3fb193f1b 100644 --- a/spec/serializers/deploy_key_entity_spec.rb +++ b/spec/serializers/deploy_key_entity_spec.rb @@ -17,6 +17,8 @@ describe DeployKeyEntity do title: deploy_key.title, fingerprint: deploy_key.fingerprint, can_push: deploy_key.can_push, + destroyed_when_orphaned: true, + almost_orphaned: false, created_at: deploy_key.created_at, updated_at: deploy_key.updated_at, projects: [ -- cgit v1.2.1 From 8c66ced7396be4a645ea530d3fcc247945203bb9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 12 Apr 2017 12:37:47 +0100 Subject: Added deployment data to metrics graphs Closes #26914 --- app/assets/javascripts/monitoring/deployments.js | 104 +++++++++++++++++++++ .../javascripts/monitoring/prometheus_graph.js | 29 +++--- app/assets/stylesheets/pages/environments.scss | 16 +--- 3 files changed, 126 insertions(+), 23 deletions(-) create mode 100644 app/assets/javascripts/monitoring/deployments.js diff --git a/app/assets/javascripts/monitoring/deployments.js b/app/assets/javascripts/monitoring/deployments.js new file mode 100644 index 00000000000..0a7b037d805 --- /dev/null +++ b/app/assets/javascripts/monitoring/deployments.js @@ -0,0 +1,104 @@ +import d3 from 'd3'; + +export default class Deployments { + constructor(width) { + this.width = width; + this.timeFormat = d3.time.format('%b %d, %Y, %H:%M%p'); + } + + init(chartData) { + this.chartData = chartData; + + this.x = d3.time.scale().range([0, this.width]); + this.x.domain(d3.extent(this.chartData, d => d.time)); + + this.getData(); + } + + getData() { + $.ajax({ + url: 'http://192.168.0.2:3000/root/hello-world/environments/21/deployments', + dataType: 'JSON', + }) + .done((data) => { + this.data = []; + + data.deployments.forEach((deployment) => { + const date = new Date(deployment.created_at); + + if (this.x(date) >= 0) { + this.data.push({ + id: deployment.id, + time: new Date(deployment.created_at), + sha: deployment.sha, + }); + } + }); + + this.plotData(); + }); + } + + plotData() { + const charts = d3.selectAll('.prometheus-graph .graph-container'); + + charts + .each((d, i) => { + const chart = d3.select(charts[0][i]); + + this.createLine(chart); + this.createDeployInfoBox(chart); + }); + } + + createLine(chart) { + chart.append('g') + .attr('class', 'deploy-info') + .selectAll('.deploy-info') + .data(this.data) + .enter() + .append('g') + .attr('class', d => `deploy-info-${d.id}`) + .attr('transform', d => `translate(${Math.floor(this.x(d.time)) + 1}, -1)`) + .append('line') + .attr('class', 'deployment-line') + .attr('stroke', '#000000') + .attr('stroke-width', '2') + .attr({ + x1: 0, + x2: 0, + y1: 0, + y2: chart.node().getBoundingClientRect().height - 22, + }); + } + + createDeployInfoBox(chart) { + this.data.forEach((d) => { + const group = chart.select(`.deploy-info-${d.id}`) + .append('svg') + .attr('class', 'rect-text-metric deploy-info-rect') + .attr('x', '5') + .attr('y', '0') + .attr('width', 100) + .attr('height', 35); + + group.append('rect') + .attr('class', 'rect-metric') + .attr('x', 0) + .attr('y', 0) + .attr('rx', 3) + .attr('width', '100%') + .attr('height', '100%') + + const text = group.append('text') + .attr('x', 5) + .attr('y', '50%') + .attr('style', 'dominant-baseline: middle;') + .text((d) => { + return `${d.sha.slice(0, 6)} - ${this.timeFormat(d.time)}`; + }); + + group.attr('width', Math.floor(text.node().getBoundingClientRect().width) + 10); + }); + } +} diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js index 844a0785bc9..0c02d42f7cb 100644 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ b/app/assets/javascripts/monitoring/prometheus_graph.js @@ -3,6 +3,7 @@ import d3 from 'd3'; import statusCodes from '~/lib/utils/http_status'; +import Deployments from './deployments'; import '../lib/utils/common_utils'; import '../flash'; @@ -25,6 +26,8 @@ class PrometheusGraph { this.width = parentContainerWidth - this.margin.left - this.margin.right; this.height = 400 - this.margin.top - this.margin.bottom; this.backOffRequestCounter = 0; + this.deployments = new Deployments(this.width); + this.configureGraph(); this.init(); } @@ -45,6 +48,7 @@ class PrometheusGraph { } else { this.transformData(metricsResponse); this.createGraph(); + this.deployments.init(this.data[Object.keys(this.data)[0]]); } }); } @@ -64,6 +68,7 @@ class PrometheusGraph { .attr('width', this.width + this.margin.left + this.margin.right) .attr('height', this.height + this.margin.bottom + this.margin.top) .append('g') + .attr('class', 'graph-container') .attr('transform', `translate(${this.margin.left},${this.margin.top})`); const axisLabelContainer = d3.select(prometheusGraphContainer) @@ -204,14 +209,13 @@ class PrometheusGraph { const d0 = valuesToPlot[timeValueIndex - 1]; const d1 = valuesToPlot[timeValueIndex]; const currentData = timeValueFromOverlay - d0.time > d1.time - timeValueFromOverlay ? d1 : d0; - const maxValueMetric = y(d3.max(valuesToPlot.map(metricValue => metricValue.value))); - const currentTimeCoordinate = x(currentData.time); + const maxValueMetric = Math.floor(y(d3.max(valuesToPlot.map(metricValue => metricValue.value)))); + const currentTimeCoordinate = Math.floor(x(currentData.time)); const graphSpecifics = this.graphSpecificProperties[key]; // Remove the current selectors d3.selectAll(`${prometheusGraphContainer} .selected-metric-line`).remove(); d3.selectAll(`${prometheusGraphContainer} .circle-metric`).remove(); - d3.selectAll(`${prometheusGraphContainer} .rect-text-metric`).remove(); - d3.selectAll(`${prometheusGraphContainer} .text-metric`).remove(); + d3.selectAll(`${prometheusGraphContainer} .rect-text-metric:not(.deploy-info-rect)`).remove(); chart.append('line') .attr('class', 'selected-metric-line') @@ -230,27 +234,28 @@ class PrometheusGraph { .attr('r', this.commonGraphProperties.circle_radius_metric); // The little box with text - const rectTextMetric = chart.append('g') + const rectTextMetric = chart.append('svg') .attr('class', 'rect-text-metric') - .attr('translate', `(${currentTimeCoordinate}, ${y(currentData.value)})`); + .attr('x', currentTimeCoordinate) + .attr('y', -1); rectTextMetric.append('rect') .attr('class', 'rect-metric') - .attr('x', currentTimeCoordinate + 10) - .attr('y', maxValueMetric) + .attr('x', 10) + .attr('y', 0) .attr('width', this.commonGraphProperties.rect_text_width) .attr('height', this.commonGraphProperties.rect_text_height); rectTextMetric.append('text') .attr('class', 'text-metric') - .attr('x', currentTimeCoordinate + 35) - .attr('y', maxValueMetric + 35) + .attr('x', 35) + .attr('y', 35) .text(timeFormat(currentData.time)); rectTextMetric.append('text') .attr('class', 'text-metric-date') - .attr('x', currentTimeCoordinate + 15) - .attr('y', maxValueMetric + 15) + .attr('x', 15) + .attr('y', 15) .text(dayFormat(currentData.time)); // Update the text diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 25be7f408d0..8d35a269fbe 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -157,7 +157,8 @@ .prometheus-graph { text { - fill: $stat-graph-axis-fill; + fill: #525252; + stroke-width: 0; } } @@ -200,22 +201,15 @@ .rect-text-metric { fill: $white-light; stroke-width: 1; - stroke: $black; + stroke: #e1e1e1; } .rect-axis-text { fill: $white-light; } -.text-metric, -.text-median-metric, -.text-metric-usage, -.text-metric-date { - fill: $black; -} - -.text-metric-date { - font-weight: 200; +.text-metric { + font-weight: 600; } .selected-metric-line { -- cgit v1.2.1 From 59616dc7505502bc1aab616099a9f37a295f7359 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 12 Apr 2017 15:31:14 +0100 Subject: Fixed up some code Improved the design Pulls the endpoint from the HAML --- app/assets/javascripts/monitoring/deployments.js | 60 ++++++++++++---------- .../javascripts/monitoring/prometheus_graph.js | 5 +- app/assets/stylesheets/pages/environments.scss | 7 ++- app/views/projects/environments/metrics.html.haml | 2 +- .../fixtures/environments/metrics.html.haml | 3 +- 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/app/assets/javascripts/monitoring/deployments.js b/app/assets/javascripts/monitoring/deployments.js index 0a7b037d805..94ad49b4dc1 100644 --- a/app/assets/javascripts/monitoring/deployments.js +++ b/app/assets/javascripts/monitoring/deployments.js @@ -1,9 +1,12 @@ import d3 from 'd3'; export default class Deployments { - constructor(width) { + constructor(width, height) { this.width = width; + this.height = height; this.timeFormat = d3.time.format('%b %d, %Y, %H:%M%p'); + + this.endpoint = document.getElementById('js-metrics').dataset.deploymentEndpoint; } init(chartData) { @@ -12,12 +15,14 @@ export default class Deployments { this.x = d3.time.scale().range([0, this.width]); this.x.domain(d3.extent(this.chartData, d => d.time)); + this.charts = d3.selectAll('.prometheus-graph .graph-container'); + this.getData(); } getData() { $.ajax({ - url: 'http://192.168.0.2:3000/root/hello-world/environments/21/deployments', + url: this.endpoint, dataType: 'JSON', }) .done((data) => { @@ -31,6 +36,8 @@ export default class Deployments { id: deployment.id, time: new Date(deployment.created_at), sha: deployment.sha, + tag: deployment.tag, + ref: deployment.ref.name, }); } }); @@ -40,15 +47,12 @@ export default class Deployments { } plotData() { - const charts = d3.selectAll('.prometheus-graph .graph-container'); - - charts - .each((d, i) => { - const chart = d3.select(charts[0][i]); + this.charts.each((d, i) => { + const chart = d3.select(this.charts[0][i]); - this.createLine(chart); - this.createDeployInfoBox(chart); - }); + this.createLine(chart); + this.createDeployInfoBox(chart); + }); } createLine(chart) { @@ -59,16 +63,14 @@ export default class Deployments { .enter() .append('g') .attr('class', d => `deploy-info-${d.id}`) - .attr('transform', d => `translate(${Math.floor(this.x(d.time)) + 1}, -1)`) + .attr('transform', d => `translate(${Math.floor(this.x(d.time)) + 1}, 0)`) .append('line') .attr('class', 'deployment-line') - .attr('stroke', '#000000') - .attr('stroke-width', '2') .attr({ x1: 0, x2: 0, y1: 0, - y2: chart.node().getBoundingClientRect().height - 22, + y2: this.height, }); } @@ -76,29 +78,31 @@ export default class Deployments { this.data.forEach((d) => { const group = chart.select(`.deploy-info-${d.id}`) .append('svg') - .attr('class', 'rect-text-metric deploy-info-rect') - .attr('x', '5') - .attr('y', '0') - .attr('width', 100) - .attr('height', 35); - - group.append('rect') - .attr('class', 'rect-metric') - .attr('x', 0) + .attr('x', 3) .attr('y', 0) - .attr('rx', 3) - .attr('width', '100%') - .attr('height', '100%') + .attr('height', 38); + + const rect = group.append('rect') + .attr('class', 'rect-text-metric deploy-info-rect rect-metric') + .attr('x', 1) + .attr('y', 1) + .attr('rx', 2) + .attr('height', 35); const text = group.append('text') .attr('x', 5) .attr('y', '50%') .attr('style', 'dominant-baseline: middle;') .text((d) => { - return `${d.sha.slice(0, 6)} - ${this.timeFormat(d.time)}`; + const isTag = d.tag; + const refText = isTag ? d.ref : d.sha.slice(0, 6); + + return `${refText} - ${this.timeFormat(d.time)}`; }); - group.attr('width', Math.floor(text.node().getBoundingClientRect().width) + 10); + group.attr('width', Math.floor(text.node().getBoundingClientRect().width) + 14); + + rect.attr('width', Math.floor(text.node().getBoundingClientRect().width) + 10); }); } } diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js index 0c02d42f7cb..bdff045887f 100644 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ b/app/assets/javascripts/monitoring/prometheus_graph.js @@ -26,7 +26,7 @@ class PrometheusGraph { this.width = parentContainerWidth - this.margin.left - this.margin.right; this.height = 400 - this.margin.top - this.margin.bottom; this.backOffRequestCounter = 0; - this.deployments = new Deployments(this.width); + this.deployments = new Deployments(this.width, this.height); this.configureGraph(); this.init(); @@ -89,6 +89,7 @@ class PrometheusGraph { .scale(y) .ticks(this.commonGraphProperties.axis_no_ticks) .tickSize(-this.width) + .outerTickSize(0) .orient('left'); this.createAxisLabelContainers(axisLabelContainer, key); @@ -237,7 +238,7 @@ class PrometheusGraph { const rectTextMetric = chart.append('svg') .attr('class', 'rect-text-metric') .attr('x', currentTimeCoordinate) - .attr('y', -1); + .attr('y', 0); rectTextMetric.append('rect') .attr('class', 'rect-metric') diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 8d35a269fbe..40905787716 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -213,6 +213,11 @@ } .selected-metric-line { - stroke: $black; + stroke: #8c8c8c; stroke-width: 1; } + +.deployment-line { + stroke: #000; + stroke-width: 2; +} diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 3b45162df52..69a7165bdbe 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -5,7 +5,7 @@ = page_specific_javascript_bundle_tag('monitoring') = render "projects/pipelines/head" -%div{ class: container_class } +#js-metrics{ class: container_class, data: { deployment_endpoint: namespace_project_environment_deployments_path(@project.namespace, @project, @environment, format: :json) } } .top-area .row .col-sm-6 diff --git a/spec/javascripts/fixtures/environments/metrics.html.haml b/spec/javascripts/fixtures/environments/metrics.html.haml index 483063fb889..e104c536fe9 100644 --- a/spec/javascripts/fixtures/environments/metrics.html.haml +++ b/spec/javascripts/fixtures/environments/metrics.html.haml @@ -1,4 +1,5 @@ %div + %svg.graph-line-shadow .top-area .row .col-sm-6 @@ -9,4 +10,4 @@ %svg.prometheus-graph{ 'graph-type' => 'cpu_values' } .row .col-sm-12 - %svg.prometheus-graph{ 'graph-type' => 'memory_values' } \ No newline at end of file + %svg.prometheus-graph{ 'graph-type' => 'memory_values' } -- cgit v1.2.1 From 69c450c09430e3854f6aa4732ffa59ac14d8fec2 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 14 Apr 2017 15:22:16 +0100 Subject: eslint fixes --- app/assets/javascripts/monitoring/deployments.js | 62 ++++++++++++++-------- .../javascripts/monitoring/prometheus_graph.js | 4 +- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/monitoring/deployments.js b/app/assets/javascripts/monitoring/deployments.js index 94ad49b4dc1..085e1869664 100644 --- a/app/assets/javascripts/monitoring/deployments.js +++ b/app/assets/javascripts/monitoring/deployments.js @@ -57,16 +57,20 @@ export default class Deployments { createLine(chart) { chart.append('g') - .attr('class', 'deploy-info') + .attr({ + class: 'deploy-info', + }) .selectAll('.deploy-info') .data(this.data) .enter() .append('g') - .attr('class', d => `deploy-info-${d.id}`) - .attr('transform', d => `translate(${Math.floor(this.x(d.time)) + 1}, 0)`) + .attr({ + class: d => `deploy-info-${d.id}`, + transform: d => `translate(${Math.floor(this.x(d.time)) + 1}, 0)`, + }) .append('line') - .attr('class', 'deployment-line') .attr({ + class: 'deployment-line', x1: 0, x2: 0, y1: 0, @@ -78,31 +82,47 @@ export default class Deployments { this.data.forEach((d) => { const group = chart.select(`.deploy-info-${d.id}`) .append('svg') - .attr('x', 3) - .attr('y', 0) - .attr('height', 38); + .attr({ + x: 3, + y: 0, + height: 41, + }); const rect = group.append('rect') - .attr('class', 'rect-text-metric deploy-info-rect rect-metric') - .attr('x', 1) - .attr('y', 1) - .attr('rx', 2) - .attr('height', 35); - - const text = group.append('text') - .attr('x', 5) - .attr('y', '50%') - .attr('style', 'dominant-baseline: middle;') - .text((d) => { + .attr({ + class: 'rect-text-metric deploy-info-rect rect-metric', + x: 1, + y: 1, + rx: 2, + height: group.attr('height') - 2, + }); + + const textGroup = group.append('g') + .attr({ + transform: 'translate(5, 2)', + }); + + textGroup.append('text') + .attr({ + style: 'dominant-baseline: text-before-edge;', + }) + .text(() => { const isTag = d.tag; const refText = isTag ? d.ref : d.sha.slice(0, 6); - return `${refText} - ${this.timeFormat(d.time)}`; + return refText; }); - group.attr('width', Math.floor(text.node().getBoundingClientRect().width) + 14); + textGroup.append('text') + .attr({ + style: 'dominant-baseline: text-before-edge; font-weight: 600;', + y: 18, + }) + .text(() => this.timeFormat(d.time)); + + group.attr('width', Math.floor(textGroup.node().getBoundingClientRect().width) + 14); - rect.attr('width', Math.floor(text.node().getBoundingClientRect().width) + 10); + rect.attr('width', Math.floor(textGroup.node().getBoundingClientRect().width) + 10); }); } } diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js index bdff045887f..98bb8a8baba 100644 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ b/app/assets/javascripts/monitoring/prometheus_graph.js @@ -210,7 +210,9 @@ class PrometheusGraph { const d0 = valuesToPlot[timeValueIndex - 1]; const d1 = valuesToPlot[timeValueIndex]; const currentData = timeValueFromOverlay - d0.time > d1.time - timeValueFromOverlay ? d1 : d0; - const maxValueMetric = Math.floor(y(d3.max(valuesToPlot.map(metricValue => metricValue.value)))); + const maxValueMetric = Math.floor( + y(d3.max(valuesToPlot.map(metricValue => metricValue.value))), + ); const currentTimeCoordinate = Math.floor(x(currentData.time)); const graphSpecifics = this.graphSpecificProperties[key]; // Remove the current selectors -- cgit v1.2.1 From da2a7823cc6b4db1bcd09153edac64d1cc2cb463 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 18 Apr 2017 10:23:01 +0100 Subject: Shows deployment box on hover --- app/assets/javascripts/monitoring/deployments.js | 52 +++++++++++++++++++--- .../javascripts/monitoring/prometheus_graph.js | 34 +++++++------- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/monitoring/deployments.js b/app/assets/javascripts/monitoring/deployments.js index 085e1869664..c41ec73b5f3 100644 --- a/app/assets/javascripts/monitoring/deployments.js +++ b/app/assets/javascripts/monitoring/deployments.js @@ -4,7 +4,8 @@ export default class Deployments { constructor(width, height) { this.width = width; this.height = height; - this.timeFormat = d3.time.format('%b %d, %Y, %H:%M%p'); + this.dateFormat = d3.time.format('%b %d, %Y'); + this.timeFormat = d3.time.format('%H:%M%p'); this.endpoint = document.getElementById('js-metrics').dataset.deploymentEndpoint; } @@ -29,15 +30,20 @@ export default class Deployments { this.data = []; data.deployments.forEach((deployment) => { - const date = new Date(deployment.created_at); + const coeff = 1000 * 60; + let time = new Date(deployment.created_at); + time = new Date(Math.round(time.getTime() / coeff) * coeff); + time.setSeconds(this.chartData[0].time.getSeconds()); + const xPos = Math.floor(this.x(time)); - if (this.x(date) >= 0) { + if (xPos >= 0) { this.data.push({ id: deployment.id, - time: new Date(deployment.created_at), + time, sha: deployment.sha, tag: deployment.tag, ref: deployment.ref.name, + xPos, }); } }); @@ -66,7 +72,7 @@ export default class Deployments { .append('g') .attr({ class: d => `deploy-info-${d.id}`, - transform: d => `translate(${Math.floor(this.x(d.time)) + 1}, 0)`, + transform: d => `translate(${Math.floor(d.xPos) + 1}, 0)`, }) .append('line') .attr({ @@ -85,7 +91,7 @@ export default class Deployments { .attr({ x: 3, y: 0, - height: 41, + height: 58, }); const rect = group.append('rect') @@ -115,14 +121,46 @@ export default class Deployments { textGroup.append('text') .attr({ - style: 'dominant-baseline: text-before-edge; font-weight: 600;', + style: 'dominant-baseline: text-before-edge;', y: 18, }) + .text(() => this.dateFormat(d.time)); + + textGroup.append('text') + .attr({ + style: 'dominant-baseline: text-before-edge;', + y: 36, + }) .text(() => this.timeFormat(d.time)); group.attr('width', Math.floor(textGroup.node().getBoundingClientRect().width) + 14); rect.attr('width', Math.floor(textGroup.node().getBoundingClientRect().width) + 10); + + group.attr('class', 'js-deploy-info-box hidden'); }); } + + static toggleDeployTextbox(deploy, showInfoBox) { + d3.selectAll(`.deploy-info-${deploy.id} .js-deploy-info-box`) + .classed('hidden', !showInfoBox); + } + + mouseOverDeployInfo(mouseXPos) { + if (!this.data) return false; + + let dataFound = false; + + this.data.forEach((d) => { + if (d.xPos >= mouseXPos - 10 && d.xPos <= mouseXPos + 10 && !dataFound) { + dataFound = true; + + Deployments.toggleDeployTextbox(d, true); + } else { + Deployments.toggleDeployTextbox(d, false); + } + }); + + return dataFound; + } } diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js index 98bb8a8baba..22e08d5ffe5 100644 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ b/app/assets/javascripts/monitoring/prometheus_graph.js @@ -9,8 +9,8 @@ import '../flash'; const prometheusGraphsContainer = '.prometheus-graph'; const metricsEndpoint = 'metrics.json'; -const timeFormat = d3.time.format('%H:%M'); -const dayFormat = d3.time.format('%b %e, %a'); +const timeFormat = d3.time.format('%H:%M%p'); +const dayFormat = d3.time.format('%b %d, %Y'); const bisectDate = d3.bisector(d => d.time).left; const extraAddedWidthParent = 100; @@ -205,7 +205,8 @@ class PrometheusGraph { handleMouseOverGraph(x, y, valuesToPlot, chart, prometheusGraphContainer, key) { const rectOverlay = document.querySelector(`${prometheusGraphContainer} .prometheus-graph-overlay`); - const timeValueFromOverlay = x.invert(d3.mouse(rectOverlay)[0]); + const mouse = d3.mouse(rectOverlay)[0]; + const timeValueFromOverlay = x.invert(mouse); const timeValueIndex = bisectDate(valuesToPlot, timeValueFromOverlay, 1); const d0 = valuesToPlot[timeValueIndex - 1]; const d1 = valuesToPlot[timeValueIndex]; @@ -215,11 +216,21 @@ class PrometheusGraph { ); const currentTimeCoordinate = Math.floor(x(currentData.time)); const graphSpecifics = this.graphSpecificProperties[key]; + const shouldHideTextMetric = this.deployments.mouseOverDeployInfo(mouse); // Remove the current selectors d3.selectAll(`${prometheusGraphContainer} .selected-metric-line`).remove(); d3.selectAll(`${prometheusGraphContainer} .circle-metric`).remove(); d3.selectAll(`${prometheusGraphContainer} .rect-text-metric:not(.deploy-info-rect)`).remove(); + chart.append('circle') + .attr('class', 'circle-metric') + .attr('fill', graphSpecifics.line_color) + .attr('cx', currentTimeCoordinate) + .attr('cy', y(currentData.value)) + .attr('r', this.commonGraphProperties.circle_radius_metric); + + if (shouldHideTextMetric) return; + chart.append('line') .attr('class', 'selected-metric-line') .attr({ @@ -229,13 +240,6 @@ class PrometheusGraph { y2: maxValueMetric, }); - chart.append('circle') - .attr('class', 'circle-metric') - .attr('fill', graphSpecifics.line_color) - .attr('cx', currentTimeCoordinate) - .attr('cy', y(currentData.value)) - .attr('r', this.commonGraphProperties.circle_radius_metric); - // The little box with text const rectTextMetric = chart.append('svg') .attr('class', 'rect-text-metric') @@ -244,20 +248,20 @@ class PrometheusGraph { rectTextMetric.append('rect') .attr('class', 'rect-metric') - .attr('x', 10) - .attr('y', 0) + .attr('x', 4) + .attr('y', 1) + .attr('rx', 2) .attr('width', this.commonGraphProperties.rect_text_width) .attr('height', this.commonGraphProperties.rect_text_height); rectTextMetric.append('text') - .attr('class', 'text-metric') - .attr('x', 35) + .attr('x', 8) .attr('y', 35) .text(timeFormat(currentData.time)); rectTextMetric.append('text') .attr('class', 'text-metric-date') - .attr('x', 15) + .attr('x', 8) .attr('y', 15) .text(dayFormat(currentData.time)); -- cgit v1.2.1 From 5d8891fb85a4646557e2f478c6c08c997868c8fb Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 18 Apr 2017 11:57:54 +0100 Subject: Added tests --- app/assets/javascripts/monitoring/deployments.js | 4 +- .../javascripts/monitoring/prometheus_graph.js | 18 ++-- .../fixtures/environments/metrics.html.haml | 2 +- spec/javascripts/monitoring/deployments_spec.js | 116 +++++++++++++++++++++ 4 files changed, 128 insertions(+), 12 deletions(-) create mode 100644 spec/javascripts/monitoring/deployments_spec.js diff --git a/app/assets/javascripts/monitoring/deployments.js b/app/assets/javascripts/monitoring/deployments.js index c41ec73b5f3..a6d7a1fbd7c 100644 --- a/app/assets/javascripts/monitoring/deployments.js +++ b/app/assets/javascripts/monitoring/deployments.js @@ -30,9 +30,9 @@ export default class Deployments { this.data = []; data.deployments.forEach((deployment) => { - const coeff = 1000 * 60; + const minInSeconds = 1000 * 60; let time = new Date(deployment.created_at); - time = new Date(Math.round(time.getTime() / coeff) * coeff); + time = new Date(Math.round(time.getTime() / minInSeconds) * minInSeconds); time.setSeconds(this.chartData[0].time.getSeconds()); const xPos = Math.floor(this.x(time)); diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js index 22e08d5ffe5..03d47c052c8 100644 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ b/app/assets/javascripts/monitoring/prometheus_graph.js @@ -222,15 +222,6 @@ class PrometheusGraph { d3.selectAll(`${prometheusGraphContainer} .circle-metric`).remove(); d3.selectAll(`${prometheusGraphContainer} .rect-text-metric:not(.deploy-info-rect)`).remove(); - chart.append('circle') - .attr('class', 'circle-metric') - .attr('fill', graphSpecifics.line_color) - .attr('cx', currentTimeCoordinate) - .attr('cy', y(currentData.value)) - .attr('r', this.commonGraphProperties.circle_radius_metric); - - if (shouldHideTextMetric) return; - chart.append('line') .attr('class', 'selected-metric-line') .attr({ @@ -240,6 +231,15 @@ class PrometheusGraph { y2: maxValueMetric, }); + chart.append('circle') + .attr('class', 'circle-metric') + .attr('fill', graphSpecifics.line_color) + .attr('cx', currentTimeCoordinate) + .attr('cy', y(currentData.value)) + .attr('r', this.commonGraphProperties.circle_radius_metric); + + if (shouldHideTextMetric) return; + // The little box with text const rectTextMetric = chart.append('svg') .attr('class', 'rect-text-metric') diff --git a/spec/javascripts/fixtures/environments/metrics.html.haml b/spec/javascripts/fixtures/environments/metrics.html.haml index e104c536fe9..858fb4d45ec 100644 --- a/spec/javascripts/fixtures/environments/metrics.html.haml +++ b/spec/javascripts/fixtures/environments/metrics.html.haml @@ -1,4 +1,4 @@ -%div +#js-metrics{ data: { endpoint: '/test' } } %svg.graph-line-shadow .top-area .row diff --git a/spec/javascripts/monitoring/deployments_spec.js b/spec/javascripts/monitoring/deployments_spec.js new file mode 100644 index 00000000000..c0e7fc9fb0e --- /dev/null +++ b/spec/javascripts/monitoring/deployments_spec.js @@ -0,0 +1,116 @@ +import d3 from 'd3'; +import PrometheusGraph from '~/monitoring/prometheus_graph'; +import Deployments from '~/monitoring/deployments'; +import { prometheusMockData } from './prometheus_mock_data'; + +fdescribe('Metrics deployments', () => { + const fixtureName = 'static/environments/metrics.html.raw'; + let deployment; + let prometheusGraph; + + const createDeploymentMockData = (done) => { + return { + deployments: [{ + id: 1, + created_at: deployment.chartData[10].time, + sha: 'testing', + tag: false, + ref: { + name: 'testing', + }, + }, { + id: 2, + created_at: deployment.chartData[15].time, + sha: '', + tag: true, + ref: { + name: 'tag', + }, + }], + }; + }; + + const graphElement = () => document.querySelector('.prometheus-graph'); + + preloadFixtures(fixtureName); + + beforeEach((done) => { + // Setup the view + loadFixtures(fixtureName); + + d3.selectAll('.prometheus-graph') + .append('g') + .attr('class', 'graph-container'); + + prometheusGraph = new PrometheusGraph(); + deployment = new Deployments(1000, 500); + + spyOn(prometheusGraph, 'init'); + spyOn($, 'ajax').and.callFake(() => { + const d = $.Deferred(); + d.resolve(createDeploymentMockData()); + + setTimeout(done); + + return d.promise(); + }); + + prometheusGraph.transformData(prometheusMockData.metrics); + + deployment.init(prometheusGraph.data.memory_values); + }); + + it('creates line on graph for deploment', () => { + expect( + graphElement().querySelectorAll('.deployment-line').length, + ).toBe(2); + }); + + it('creates hidden deploy boxes', () => { + expect( + graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box').length, + ).toBe(2); + }); + + it('hides the info boxes by default', () => { + expect( + graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box.hidden').length, + ).toBe(2); + }); + + it('shows sha short code when tag is false', () => { + expect( + graphElement().querySelector('.deploy-info-1 .js-deploy-info-box').textContent.trim(), + ).toContain('testin'); + }); + + it('shows ref name when tag is true', () => { + expect( + graphElement().querySelector('.deploy-info-2 .js-deploy-info-box').textContent.trim(), + ).toContain('tag'); + }); + + it('shows info box when moving mouse over line', () => { + deployment.mouseOverDeployInfo(deployment.data[0].xPos); + + expect( + graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box.hidden').length, + ).toBe(1); + + expect( + graphElement().querySelector('.deploy-info-1 .js-deploy-info-box.hidden'), + ).toBeNull(); + }); + + it('hides previously visible info box when moving mouse away', () => { + deployment.mouseOverDeployInfo(500); + + expect( + graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box.hidden').length, + ).toBe(2); + + expect( + graphElement().querySelector('.deploy-info-1 .js-deploy-info-box.hidden'), + ).not.toBeNull(); + }); +}); -- cgit v1.2.1 From 8d3b2581b8e1f3aff82fb8bf76203430fde064b3 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Apr 2017 12:06:14 +0100 Subject: Style improvements Fixed an issue where the line wouldnt move when hovering over deployment --- app/assets/javascripts/monitoring/deployments.js | 51 ++++++++++++++++++++-- .../javascripts/monitoring/prometheus_graph.js | 8 ++-- app/assets/stylesheets/pages/environments.scss | 16 +++++-- spec/javascripts/monitoring/deployments_spec.js | 44 +++++++++---------- 4 files changed, 83 insertions(+), 36 deletions(-) diff --git a/app/assets/javascripts/monitoring/deployments.js b/app/assets/javascripts/monitoring/deployments.js index a6d7a1fbd7c..aa75cdcaeaf 100644 --- a/app/assets/javascripts/monitoring/deployments.js +++ b/app/assets/javascripts/monitoring/deployments.js @@ -8,6 +8,8 @@ export default class Deployments { this.timeFormat = d3.time.format('%H:%M%p'); this.endpoint = document.getElementById('js-metrics').dataset.deploymentEndpoint; + + Deployments.createGradientDef(); } init(chartData) { @@ -61,6 +63,36 @@ export default class Deployments { }); } + static createGradientDef() { + const defs = d3.select('body') + .append('svg') + .attr({ + height: 0, + width: 0, + }) + .append('defs'); + + defs.append('linearGradient') + .attr({ + id: 'shadow-gradient', + }) + .append('stop') + .attr({ + offset: '0%', + 'stop-color': '#000', + 'stop-opacity': 0.4, + }) + .select(function selectParentNode() { + return this.parentNode; + }) + .append('stop') + .attr({ + offset: '100%', + 'stop-color': '#000', + 'stop-opacity': 0, + }); + } + createLine(chart) { chart.append('g') .attr({ @@ -74,6 +106,17 @@ export default class Deployments { class: d => `deploy-info-${d.id}`, transform: d => `translate(${Math.floor(d.xPos) + 1}, 0)`, }) + .append('rect') + .attr({ + x: 1, + y: 0, + height: this.height, + width: 3, + fill: 'url(#shadow-gradient)', + }) + .select(function selectParentNode() { + return this.parentNode; + }) .append('line') .attr({ class: 'deployment-line', @@ -110,7 +153,7 @@ export default class Deployments { textGroup.append('text') .attr({ - style: 'dominant-baseline: text-before-edge;', + class: 'deploy-info-text deploy-info-text-bold', }) .text(() => { const isTag = d.tag; @@ -121,14 +164,14 @@ export default class Deployments { textGroup.append('text') .attr({ - style: 'dominant-baseline: text-before-edge;', + class: 'deploy-info-text', y: 18, }) .text(() => this.dateFormat(d.time)); textGroup.append('text') .attr({ - style: 'dominant-baseline: text-before-edge;', + class: 'deploy-info-text', y: 36, }) .text(() => this.timeFormat(d.time)); @@ -153,7 +196,7 @@ export default class Deployments { this.data.forEach((d) => { if (d.xPos >= mouseXPos - 10 && d.xPos <= mouseXPos + 10 && !dataFound) { - dataFound = true; + dataFound = d.xPos + 1; Deployments.toggleDeployTextbox(d, true); } else { diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js index 03d47c052c8..212fbce91ba 100644 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ b/app/assets/javascripts/monitoring/prometheus_graph.js @@ -214,17 +214,17 @@ class PrometheusGraph { const maxValueMetric = Math.floor( y(d3.max(valuesToPlot.map(metricValue => metricValue.value))), ); + const currentDeployXPos = this.deployments.mouseOverDeployInfo(mouse); const currentTimeCoordinate = Math.floor(x(currentData.time)); const graphSpecifics = this.graphSpecificProperties[key]; - const shouldHideTextMetric = this.deployments.mouseOverDeployInfo(mouse); // Remove the current selectors d3.selectAll(`${prometheusGraphContainer} .selected-metric-line`).remove(); d3.selectAll(`${prometheusGraphContainer} .circle-metric`).remove(); d3.selectAll(`${prometheusGraphContainer} .rect-text-metric:not(.deploy-info-rect)`).remove(); chart.append('line') - .attr('class', 'selected-metric-line') .attr({ + class: `${currentDeployXPos ? 'hidden' : ''} selected-metric-line`, x1: currentTimeCoordinate, y1: y(0), x2: currentTimeCoordinate, @@ -234,11 +234,11 @@ class PrometheusGraph { chart.append('circle') .attr('class', 'circle-metric') .attr('fill', graphSpecifics.line_color) - .attr('cx', currentTimeCoordinate) + .attr('cx', currentDeployXPos || currentTimeCoordinate) .attr('cy', y(currentData.value)) .attr('r', this.commonGraphProperties.circle_radius_metric); - if (shouldHideTextMetric) return; + if (currentDeployXPos) return; // The little box with text const rectTextMetric = chart.append('svg') diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 40905787716..30e665213b6 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -157,7 +157,7 @@ .prometheus-graph { text { - fill: #525252; + fill: $gl-text-color; stroke-width: 0; } } @@ -201,7 +201,7 @@ .rect-text-metric { fill: $white-light; stroke-width: 1; - stroke: #e1e1e1; + stroke: $gray-darkest; } .rect-axis-text { @@ -213,11 +213,19 @@ } .selected-metric-line { - stroke: #8c8c8c; + stroke: $gl-gray-dark; stroke-width: 1; } .deployment-line { - stroke: #000; + stroke: $black; stroke-width: 2; } + +.deploy-info-text { + dominant-baseline: text-before-edge; +} + +.deploy-info-text-bold { + font-weight: 600; +} diff --git a/spec/javascripts/monitoring/deployments_spec.js b/spec/javascripts/monitoring/deployments_spec.js index c0e7fc9fb0e..b091b0550aa 100644 --- a/spec/javascripts/monitoring/deployments_spec.js +++ b/spec/javascripts/monitoring/deployments_spec.js @@ -3,33 +3,11 @@ import PrometheusGraph from '~/monitoring/prometheus_graph'; import Deployments from '~/monitoring/deployments'; import { prometheusMockData } from './prometheus_mock_data'; -fdescribe('Metrics deployments', () => { +describe('Metrics deployments', () => { const fixtureName = 'static/environments/metrics.html.raw'; let deployment; let prometheusGraph; - const createDeploymentMockData = (done) => { - return { - deployments: [{ - id: 1, - created_at: deployment.chartData[10].time, - sha: 'testing', - tag: false, - ref: { - name: 'testing', - }, - }, { - id: 2, - created_at: deployment.chartData[15].time, - sha: '', - tag: true, - ref: { - name: 'tag', - }, - }], - }; - }; - const graphElement = () => document.querySelector('.prometheus-graph'); preloadFixtures(fixtureName); @@ -48,7 +26,25 @@ fdescribe('Metrics deployments', () => { spyOn(prometheusGraph, 'init'); spyOn($, 'ajax').and.callFake(() => { const d = $.Deferred(); - d.resolve(createDeploymentMockData()); + d.resolve({ + deployments: [{ + id: 1, + created_at: deployment.chartData[10].time, + sha: 'testing', + tag: false, + ref: { + name: 'testing', + }, + }, { + id: 2, + created_at: deployment.chartData[15].time, + sha: '', + tag: true, + ref: { + name: 'tag', + }, + }], + }); setTimeout(done); -- cgit v1.2.1 From 446994610546bcb43d6ace34c1c28357de306b19 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 20 Apr 2017 11:58:31 +0200 Subject: Reduce the amount of data deployment endpoint returns --- app/serializers/deployment_entity.rb | 11 ----------- app/serializers/deployment_entity_detailed.rb | 28 +++++++++++++++++++++++++++ app/serializers/environment_entity.rb | 2 +- spec/serializers/deployment_entity_spec.rb | 2 +- 4 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 app/serializers/deployment_entity_detailed.rb diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index d9ade5d87e2..a8d6d80faad 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -9,20 +9,9 @@ class DeploymentEntity < Grape::Entity expose :name do |deployment| deployment.ref end - - expose :ref_path do |deployment| - namespace_project_tree_path( - deployment.project.namespace, - deployment.project, - id: deployment.ref) - end end expose :created_at expose :tag expose :last? - expose :user, using: UserEntity - expose :commit, using: CommitEntity - expose :deployable, using: BuildEntity - expose :manual_actions, using: BuildEntity end diff --git a/app/serializers/deployment_entity_detailed.rb b/app/serializers/deployment_entity_detailed.rb new file mode 100644 index 00000000000..a0de0f9666b --- /dev/null +++ b/app/serializers/deployment_entity_detailed.rb @@ -0,0 +1,28 @@ +class DeploymentEntityDetailed < Grape::Entity + include RequestAwareEntity + + expose :id + expose :iid + expose :sha + + expose :ref do + expose :name do |deployment| + deployment.ref + end + + expose :ref_path do |deployment| + namespace_project_tree_path( + deployment.project.namespace, + deployment.project, + id: deployment.ref) + end + end + + expose :created_at + expose :tag + expose :last? + expose :user, using: UserEntity + expose :commit, using: CommitEntity + expose :deployable, using: BuildEntity + expose :manual_actions, using: BuildEntity +end diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb index 4ff15a78115..2152a1dd499 100644 --- a/app/serializers/environment_entity.rb +++ b/app/serializers/environment_entity.rb @@ -6,7 +6,7 @@ class EnvironmentEntity < Grape::Entity expose :state expose :external_url expose :environment_type - expose :last_deployment, using: DeploymentEntity + expose :last_deployment, using: DeploymentEntityDetailed expose :stop_action? expose :metrics_path, if: -> (environment, _) { environment.has_metrics? } do |environment| diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb index 73bd60b62e5..815837c13b0 100644 --- a/spec/serializers/deployment_entity_spec.rb +++ b/spec/serializers/deployment_entity_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DeploymentEntity do +describe DeploymentEntityDetailed do let(:user) { create(:user) } let(:request) { double('request') } -- cgit v1.2.1 From e8f2daae8e5eaa6d5ff6a58f95b89d267141a475 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 20 Apr 2017 12:21:24 +0200 Subject: Cleanup duplicates in deploymentEntityDetailed and add small spec tests --- app/serializers/deployment_entity_detailed.rb | 15 +------------ .../serializers/deployment_entity_detailed_spec.rb | 26 ++++++++++++++++++++++ spec/serializers/deployment_entity_spec.rb | 14 ++++-------- 3 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 spec/serializers/deployment_entity_detailed_spec.rb diff --git a/app/serializers/deployment_entity_detailed.rb b/app/serializers/deployment_entity_detailed.rb index a0de0f9666b..63df61dd51b 100644 --- a/app/serializers/deployment_entity_detailed.rb +++ b/app/serializers/deployment_entity_detailed.rb @@ -1,15 +1,5 @@ -class DeploymentEntityDetailed < Grape::Entity - include RequestAwareEntity - - expose :id - expose :iid - expose :sha - +class DeploymentEntityDetailed < DeploymentEntity expose :ref do - expose :name do |deployment| - deployment.ref - end - expose :ref_path do |deployment| namespace_project_tree_path( deployment.project.namespace, @@ -18,9 +8,6 @@ class DeploymentEntityDetailed < Grape::Entity end end - expose :created_at - expose :tag - expose :last? expose :user, using: UserEntity expose :commit, using: CommitEntity expose :deployable, using: BuildEntity diff --git a/spec/serializers/deployment_entity_detailed_spec.rb b/spec/serializers/deployment_entity_detailed_spec.rb new file mode 100644 index 00000000000..d28e3fa0665 --- /dev/null +++ b/spec/serializers/deployment_entity_detailed_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe DeploymentEntityDetailed do + let(:user) { create(:user) } + let(:request) { double('request') } + let(:deployment) { create(:deployment) } + let(:entity) { described_class.new(deployment, request: request) } + subject { entity.as_json } + + before do + allow(request).to receive(:user).and_return(user) + end + + it 'exposes internal deployment id' do + expect(subject).to include(:iid) + end + + it 'exposes nested information about branch' do + expect(subject[:ref][:name]).to eq 'master' + expect(subject[:ref][:ref_path]).not_to be_empty + end + + it 'exposes creation date' do + expect(subject).to include(:created_at) + end +end diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb index 815837c13b0..69355bcde42 100644 --- a/spec/serializers/deployment_entity_spec.rb +++ b/spec/serializers/deployment_entity_spec.rb @@ -1,28 +1,22 @@ require 'spec_helper' -describe DeploymentEntityDetailed do +describe DeploymentEntity do let(:user) { create(:user) } let(:request) { double('request') } + let(:deployment) { create(:deployment) } + let(:entity) { described_class.new(deployment, request: request) } + subject { entity.as_json } before do allow(request).to receive(:user).and_return(user) end - let(:entity) do - described_class.new(deployment, request: request) - end - - let(:deployment) { create(:deployment) } - - subject { entity.as_json } - it 'exposes internal deployment id' do expect(subject).to include(:iid) end it 'exposes nested information about branch' do expect(subject[:ref][:name]).to eq 'master' - expect(subject[:ref][:ref_path]).not_to be_empty end it 'exposes creation date' do -- cgit v1.2.1 From 05dc14074b78822ff460f703eab0a2082d2adf46 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 21 Apr 2017 10:27:03 +0100 Subject: Updated specs --- spec/javascripts/monitoring/deployments_spec.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/spec/javascripts/monitoring/deployments_spec.js b/spec/javascripts/monitoring/deployments_spec.js index b091b0550aa..2bcec8dcc2b 100644 --- a/spec/javascripts/monitoring/deployments_spec.js +++ b/spec/javascripts/monitoring/deployments_spec.js @@ -51,9 +51,10 @@ describe('Metrics deployments', () => { return d.promise(); }); + prometheusGraph.configureGraph(); prometheusGraph.transformData(prometheusMockData.metrics); - deployment.init(prometheusGraph.data.memory_values); + deployment.init(prometheusGraph.graphSpecificProperties.memory_values.data); }); it('creates line on graph for deploment', () => { @@ -76,37 +77,37 @@ describe('Metrics deployments', () => { it('shows sha short code when tag is false', () => { expect( - graphElement().querySelector('.deploy-info-1 .js-deploy-info-box').textContent.trim(), + graphElement().querySelector('.deploy-info-1-cpu_values .js-deploy-info-box').textContent.trim(), ).toContain('testin'); }); it('shows ref name when tag is true', () => { expect( - graphElement().querySelector('.deploy-info-2 .js-deploy-info-box').textContent.trim(), + graphElement().querySelector('.deploy-info-2-cpu_values .js-deploy-info-box').textContent.trim(), ).toContain('tag'); }); it('shows info box when moving mouse over line', () => { - deployment.mouseOverDeployInfo(deployment.data[0].xPos); + deployment.mouseOverDeployInfo(deployment.data[0].xPos, 'cpu_values'); expect( graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box.hidden').length, ).toBe(1); expect( - graphElement().querySelector('.deploy-info-1 .js-deploy-info-box.hidden'), + graphElement().querySelector('.deploy-info-1-cpu_values .js-deploy-info-box.hidden'), ).toBeNull(); }); it('hides previously visible info box when moving mouse away', () => { - deployment.mouseOverDeployInfo(500); + deployment.mouseOverDeployInfo(500, 'cpu_values'); expect( graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box.hidden').length, ).toBe(2); expect( - graphElement().querySelector('.deploy-info-1 .js-deploy-info-box.hidden'), + graphElement().querySelector('.deploy-info-1-cpu_values .js-deploy-info-box.hidden'), ).not.toBeNull(); }); }); -- cgit v1.2.1 From d7154552b65c767265a8182fee463fbb5252f93b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 21 Apr 2017 11:30:40 +0100 Subject: Rather than looping data ourselves, d3 loops it --- app/assets/javascripts/monitoring/deployments.js | 134 +++++++++++------------ spec/javascripts/monitoring/deployments_spec.js | 20 ++++ 2 files changed, 86 insertions(+), 68 deletions(-) diff --git a/app/assets/javascripts/monitoring/deployments.js b/app/assets/javascripts/monitoring/deployments.js index a60291005b8..93453771aca 100644 --- a/app/assets/javascripts/monitoring/deployments.js +++ b/app/assets/javascripts/monitoring/deployments.js @@ -4,12 +4,13 @@ export default class Deployments { constructor(width, height) { this.width = width; this.height = height; + this.data = []; this.dateFormat = d3.time.format('%b %d, %Y'); this.timeFormat = d3.time.format('%H:%M%p'); this.endpoint = document.getElementById('js-metrics').dataset.deploymentEndpoint; - Deployments.createGradientDef(); + this.createGradientDef(); } init(chartData) { @@ -29,15 +30,12 @@ export default class Deployments { dataType: 'JSON', }) .done((data) => { - this.data = []; - data.deployments.forEach((deployment) => { - const minInSeconds = 1000 * 60; - let time = new Date(deployment.created_at); - time = new Date(Math.round(time.getTime() / minInSeconds) * minInSeconds); - time.setSeconds(this.chartData[0].time.getSeconds()); + const time = new Date(deployment.created_at); const xPos = Math.floor(this.x(time)); + time.setSeconds(this.chartData[0].time.getSeconds()); + if (xPos >= 0) { this.data.push({ id: deployment.id, @@ -65,7 +63,7 @@ export default class Deployments { }); } - static createGradientDef() { + createGradientDef() { const defs = d3.select('body') .append('svg') .attr({ @@ -84,9 +82,7 @@ export default class Deployments { 'stop-color': '#000', 'stop-opacity': 0.4, }) - .select(function selectParentNode() { - return this.parentNode; - }) + .select(this.selectParentNode) .append('stop') .attr({ offset: '100%', @@ -116,9 +112,7 @@ export default class Deployments { width: 3, fill: 'url(#shadow-gradient)', }) - .select(function selectParentNode() { - return this.parentNode; - }) + .select(this.selectParentNode) .append('line') .attr({ class: 'deployment-line', @@ -130,60 +124,52 @@ export default class Deployments { } createDeployInfoBox(chart, key) { - this.data.forEach((d) => { - const group = chart.select(`.deploy-info-${d.id}-${key}`) - .append('svg') - .attr({ - x: 3, - y: 0, - height: 60, - }); - - const rect = group.append('rect') - .attr({ - class: 'rect-text-metric deploy-info-rect rect-metric', - x: 1, - y: 1, - rx: 2, - height: group.attr('height') - 2, - }); - - const textGroup = group.append('g') - .attr({ - transform: 'translate(5, 2)', - }); - - textGroup.append('text') - .attr({ - class: 'deploy-info-text text-metric-bold', - }) - .text(() => { - const isTag = d.tag; - const refText = isTag ? d.ref : d.sha.slice(0, 6); - - return refText; - }); - - textGroup.append('text') - .attr({ - class: 'deploy-info-text', - y: 18, - }) - .text(() => this.dateFormat(d.time)); - - textGroup.append('text') - .attr({ - class: 'deploy-info-text text-metric-bold', - y: 38, - }) - .text(() => this.timeFormat(d.time)); - - group.attr('width', Math.floor(textGroup.node().getBoundingClientRect().width) + 14); - - rect.attr('width', Math.floor(textGroup.node().getBoundingClientRect().width) + 10); - - group.attr('class', 'js-deploy-info-box hidden'); - }); + chart.selectAll('.deploy-info') + .selectAll('.js-deploy-info-box') + .data(this.data) + .enter() + .select(d => document.querySelector(`.deploy-info-${d.id}-${key}`)) + .append('svg') + .attr({ + class: 'js-deploy-info-box hidden', + x: 3, + y: 0, + width: 92, + height: 60, + }) + .append('rect') + .attr({ + class: 'rect-text-metric deploy-info-rect rect-metric', + x: 1, + y: 1, + rx: 2, + width: 90, + height: 58, + }) + .select(this.selectParentNode) + .append('g') + .attr({ + transform: 'translate(5, 2)', + }) + .append('text') + .attr({ + class: 'deploy-info-text text-metric-bold', + }) + .text(Deployments.refText) + .select(this.selectParentNode) + .append('text') + .attr({ + class: 'deploy-info-text', + y: 18, + }) + .text(d => this.dateFormat(d.time)) + .select(this.selectParentNode) + .append('text') + .attr({ + class: 'deploy-info-text text-metric-bold', + y: 38, + }) + .text(d => this.timeFormat(d.time)); } static toggleDeployTextbox(deploy, key, showInfoBox) { @@ -208,4 +194,16 @@ export default class Deployments { return dataFound; } + + /* `this` is bound to the D3 node */ + selectParentNode() { + return this.parentNode; + } + + static refText(d) { + const isTag = d.tag; + const refText = isTag ? d.ref : d.sha.slice(0, 6); + + return refText; + } } diff --git a/spec/javascripts/monitoring/deployments_spec.js b/spec/javascripts/monitoring/deployments_spec.js index 2bcec8dcc2b..5b88f6d95a3 100644 --- a/spec/javascripts/monitoring/deployments_spec.js +++ b/spec/javascripts/monitoring/deployments_spec.js @@ -110,4 +110,24 @@ describe('Metrics deployments', () => { graphElement().querySelector('.deploy-info-1-cpu_values .js-deploy-info-box.hidden'), ).not.toBeNull(); }); + + describe('refText', () => { + it('returns shortened SHA', () => { + expect( + Deployments.refText({ + tag: false, + sha: '123456789', + }), + ).toBe('123456'); + }); + + it('returns tag name', () => { + expect( + Deployments.refText({ + tag: true, + ref: 'v1.0', + }), + ).toBe('v1.0'); + }); + }); }); -- cgit v1.2.1 From 88f69cbd8cd89ffbdc1f0200d5c298f7c7c0acd7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 21 Apr 2017 12:33:31 +0100 Subject: Used reduce over forEach Moved shared date & time formats into constants --- app/assets/javascripts/monitoring/constants.js | 4 ++++ app/assets/javascripts/monitoring/deployments.js | 26 ++++++++++++---------- .../javascripts/monitoring/prometheus_graph.js | 10 +++++---- 3 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 app/assets/javascripts/monitoring/constants.js diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js new file mode 100644 index 00000000000..c3a8da52404 --- /dev/null +++ b/app/assets/javascripts/monitoring/constants.js @@ -0,0 +1,4 @@ +import d3 from 'd3'; + +export const dateFormat = d3.time.format('%b %d, %Y'); +export const timeFormat = d3.time.format('%H:%M%p'); diff --git a/app/assets/javascripts/monitoring/deployments.js b/app/assets/javascripts/monitoring/deployments.js index 93453771aca..fc92ab61b31 100644 --- a/app/assets/javascripts/monitoring/deployments.js +++ b/app/assets/javascripts/monitoring/deployments.js @@ -1,12 +1,14 @@ +/* global Flash */ import d3 from 'd3'; +import { + dateFormat, + timeFormat, +} from './constants'; export default class Deployments { constructor(width, height) { this.width = width; this.height = height; - this.data = []; - this.dateFormat = d3.time.format('%b %d, %Y'); - this.timeFormat = d3.time.format('%H:%M%p'); this.endpoint = document.getElementById('js-metrics').dataset.deploymentEndpoint; @@ -29,15 +31,16 @@ export default class Deployments { url: this.endpoint, dataType: 'JSON', }) + .fail(() => new Flash('Error getting deployment information.')) .done((data) => { - data.deployments.forEach((deployment) => { + this.data = data.deployments.reduce((deploymentDataArray, deployment) => { const time = new Date(deployment.created_at); const xPos = Math.floor(this.x(time)); time.setSeconds(this.chartData[0].time.getSeconds()); if (xPos >= 0) { - this.data.push({ + deploymentDataArray.push({ id: deployment.id, time, sha: deployment.sha, @@ -46,7 +49,9 @@ export default class Deployments { xPos, }); } - }); + + return deploymentDataArray; + }, []); this.plotData(); }); @@ -162,14 +167,14 @@ export default class Deployments { class: 'deploy-info-text', y: 18, }) - .text(d => this.dateFormat(d.time)) + .text(d => dateFormat(d.time)) .select(this.selectParentNode) .append('text') .attr({ class: 'deploy-info-text text-metric-bold', y: 38, }) - .text(d => this.timeFormat(d.time)); + .text(d => timeFormat(d.time)); } static toggleDeployTextbox(deploy, key, showInfoBox) { @@ -201,9 +206,6 @@ export default class Deployments { } static refText(d) { - const isTag = d.tag; - const refText = isTag ? d.ref : d.sha.slice(0, 6); - - return refText; + return d.tag ? d.ref : d.sha.slice(0, 6); } } diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js index ef3211e9586..681111455d1 100644 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ b/app/assets/javascripts/monitoring/prometheus_graph.js @@ -7,14 +7,16 @@ import Deployments from './deployments'; import '../lib/utils/common_utils'; import { formatRelevantDigits } from '../lib/utils/number_utils'; import '../flash'; +import { + dateFormat, + timeFormat, +} from './constants'; const prometheusContainer = '.prometheus-container'; const prometheusParentGraphContainer = '.prometheus-graphs'; const prometheusGraphsContainer = '.prometheus-graph'; const prometheusStatesContainer = '.prometheus-state'; const metricsEndpoint = 'metrics.json'; -const timeFormat = d3.time.format('%H:%M%p'); -const dayFormat = d3.time.format('%b %d, %Y'); const bisectDate = d3.bisector(d => d.time).left; const extraAddedWidthParent = 100; @@ -256,7 +258,7 @@ class PrometheusGraph { const evalTime = timeValueOverlay - d0.time > d1.time - timeValueOverlay; const currentData = evalTime ? d1 : d0; const currentTimeCoordinate = Math.floor(currentGraphProps.xScale(currentData.time)); - const currentDeployXPos = this.deployments.mouseOverDeployInfo(currentTimeCoordinate, key); + const currentDeployXPos = this.deployments.mouseOverDeployInfo(currentXCoordinate, key); const currentPrometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`; const maxValueFromData = d3.max(currentGraphProps.data.map(metricValue => metricValue.value)); const maxMetricValue = currentGraphProps.yScale(maxValueFromData); @@ -317,7 +319,7 @@ class PrometheusGraph { x: 8, y: 15, }) - .text(dayFormat(currentData.time)); + .text(dateFormat(currentData.time)); let currentMetricValue = formatRelevantDigits(currentData.value); if (key === 'cpu_values') { -- cgit v1.2.1 From dc2f702a0475fcb44c8af7201216bd0470bff8e6 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 24 Apr 2017 14:24:48 +0100 Subject: Added emoji description title to award emoji buttons Closes #29971 --- app/assets/javascripts/awards_handler.js | 17 +- changelogs/unreleased/emoji-button-titles.yml | 4 + fixtures/emojis/digests.json | 1791 +++++++++++++++++++++++++ lib/tasks/gemojione.rake | 3 +- 4 files changed, 1809 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/emoji-button-titles.yml diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index adb45b0606d..e0c592ef73d 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -29,11 +29,18 @@ const categoryLabelMap = { flags: 'Flags', }; +function createEmojiObject(alias) { + return { + alias, + description: emojiMap[alias].description, + }; +}; + function buildCategoryMap() { return Object.keys(emojiMap).reduce((currentCategoryMap, emojiNameKey) => { const emojiInfo = emojiMap[emojiNameKey]; if (currentCategoryMap[emojiInfo.category]) { - currentCategoryMap[emojiInfo.category].push(emojiNameKey); + currentCategoryMap[emojiInfo.category].push(createEmojiObject(emojiNameKey)); } return currentCategoryMap; @@ -55,10 +62,10 @@ function renderCategory(name, emojiList, opts = {}) { ${name}
    - ${emojiList.map(emojiName => ` + ${emojiList.map(emoji => `
  • - @@ -498,7 +505,7 @@ AwardsHandler.prototype.getFrequentlyUsedEmojis = function getFrequentlyUsedEmoj const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(',')); this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter( inputName => isEmojiNameValid(inputName), - ); + ).map(emojiNameKey => createEmojiObject(emojiNameKey)); return this.frequentlyUsedEmojis; })(); diff --git a/changelogs/unreleased/emoji-button-titles.yml b/changelogs/unreleased/emoji-button-titles.yml new file mode 100644 index 00000000000..c8e1b2c6c6b --- /dev/null +++ b/changelogs/unreleased/emoji-button-titles.yml @@ -0,0 +1,4 @@ +--- +title: Added title to award emoji buttons +merge_request: +author: diff --git a/fixtures/emojis/digests.json b/fixtures/emojis/digests.json index 3cbc4702dac..589cff165f3 100644 --- a/fixtures/emojis/digests.json +++ b/fixtures/emojis/digests.json @@ -2,10746 +2,12537 @@ "100": { "category": "symbols", "moji": "💯", + "description": "hundred points symbol", "unicodeVersion": "6.0", "digest": "add3bd7d06b6dd445788b277f8c9e5dcf42a54d3ec8b7fb9e7a39695dd95d094" }, "1234": { "category": "symbols", "moji": "🔢", + "description": "input symbol for numbers", "unicodeVersion": "6.0", "digest": "c5ac5c8147f5bfd644fad6b470432bba86ffc7bcee04a0e0d277cd1ca485207f" }, "8ball": { "category": "activity", "moji": "🎱", + "description": "billiards", "unicodeVersion": "6.0", "digest": "a6e6855775b66c505adee65926a264103ebddf2e2d963db7c009b4fec3a24178" }, "a": { "category": "symbols", "moji": "🅰", + "description": "negative squared latin capital letter a", "unicodeVersion": "6.0", "digest": "bddbb39e8a1d35d42b7c08e7d47f63988cb4d8614b79f74e70b9c67c221896cc" }, "ab": { "category": "symbols", "moji": "🆎", + "description": "negative squared ab", "unicodeVersion": "6.0", "digest": "67430fe5fce981160e2ea9052962e49f264322d3abfc2828fbc311b6cdf67ae8" }, "abc": { "category": "symbols", "moji": "🔤", + "description": "input symbol for latin letters", "unicodeVersion": "6.0", "digest": "282c817ee3414d77a74b815962c33dd9fe71fabaea8c7a9cec466100fbe32187" }, "abcd": { "category": "symbols", "moji": "🔡", + "description": "input symbol for latin small letters", "unicodeVersion": "6.0", "digest": "686728c759f4683c64762ee4eda0a91bf2041f0ae4f358aacf6c09bf51892eff" }, "accept": { "category": "symbols", "moji": "🉑", + "description": "circled ideograph accept", "unicodeVersion": "6.0", "digest": "7208d34c761f10a7fd28f98e25535eba13ff91a64442fc282a98bb77722614f1" }, "aerial_tramway": { "category": "travel", "moji": "🚡", + "description": "aerial tramway", "unicodeVersion": "6.0", "digest": "98df666f34370fc34ce280d84bba5a7e617f733fbbfe66caa424b2afa6ab6777" }, "airplane": { "category": "travel", "moji": "✈", + "description": "airplane", "unicodeVersion": "1.1", "digest": "cc12cf259ef88e57717620cd2bd5aa6a02a8631ee532a3bde24bee78edc5de33" }, "airplane_arriving": { "category": "travel", "moji": "🛬", + "description": "airplane arriving", "unicodeVersion": "7.0", "digest": "80d5b4675f91c4cff06d146d795a065b0ce2a74557df4d9e3314e3d3b5c4ae82" }, "airplane_departure": { "category": "travel", "moji": "🛫", + "description": "airplane departure", "unicodeVersion": "7.0", "digest": "5544eace06b8e1b6ea91940e893e013d33d6b166e14e6d128a87f2cd2de88332" }, "airplane_small": { "category": "travel", "moji": "🛩", + "description": "small airplane", "unicodeVersion": "7.0", "digest": "1a2e07abbbe90d05cee7ff8dd52f443d595ccb38959f3089fe016b77e5d6de7d" }, "alarm_clock": { "category": "objects", "moji": "⏰", + "description": "alarm clock", "unicodeVersion": "6.0", "digest": "fef05a3cd1cddbeca4de8091b94bddb93790b03fa213da86c0eec420f8c49599" }, "alembic": { "category": "objects", "moji": "⚗", + "description": "alembic", "unicodeVersion": "4.1", "digest": "c94b2a4bf24ccf4db27a22c9725cfe900f4a99ec49ef2411d67952bcb2ca1bfb" }, "alien": { "category": "people", "moji": "👽", + "description": "extraterrestrial alien", "unicodeVersion": "6.0", "digest": "856ba98202b244c13a5ee3014a6f7ad592d8c119a30d79e4fc790b74b0e321f7" }, "ambulance": { "category": "travel", "moji": "🚑", + "description": "ambulance", "unicodeVersion": "6.0", "digest": "d9b3c1873de496a4554e715342c72290fb69a9c6766d7885f38bfe9491d052da" }, "amphora": { "category": "objects", "moji": "🏺", + "description": "amphora", "unicodeVersion": "8.0", "digest": "4015f907b649b5e348502cc0e3685ed184e180dca5cc81c43ec516e14df127bf" }, "anchor": { "category": "travel", "moji": "⚓", + "description": "anchor", "unicodeVersion": "4.1", "digest": "2b29b34ef896ebab70016301e3d1880209bbc3c5a5b8d832e43afff9b17ad792" }, "angel": { "category": "people", "moji": "👼", + "description": "baby angel", "unicodeVersion": "6.0", "digest": "db75c2460aaf9cd07cb41fe22c8a6079f3667ffe612a71611358720e2b5512a4" }, "angel_tone1": { "category": "people", "moji": "👼🏻", + "description": "baby angel tone 1", "unicodeVersion": "8.0", "digest": "5871a622469b96296365adaf77d83167759692124c20e5a6e062a525af33472a" }, "angel_tone2": { "category": "people", "moji": "👼🏼", + "description": "baby angel tone 2", "unicodeVersion": "8.0", "digest": "f5993198a5d9daf39e761c783461f07bca237f4e9b739ac300bb8ca001a69a1a" }, "angel_tone3": { "category": "people", "moji": "👼🏽", + "description": "baby angel tone 3", "unicodeVersion": "8.0", "digest": "f0c97a7c4354626267d6ab0f388e4297ad255ab9b061f9c68fbcaa0abfc52783" }, "angel_tone4": { "category": "people", "moji": "👼🏾", + "description": "baby angel tone 4", "unicodeVersion": "8.0", "digest": "6e5dc724c1939d1b0d1a91343662b5bd61ced7709c97802977145ffab6a1f7ac" }, "angel_tone5": { "category": "people", "moji": "👼🏿", + "description": "baby angel tone 5", "unicodeVersion": "8.0", "digest": "52186e1de350c27d25d6010edf44f64a30338b65912ca178429fbcfbd88113c2" }, "anger": { "category": "symbols", "moji": "💢", + "description": "anger symbol", "unicodeVersion": "6.0", "digest": "332493913891aa0eda2743b4bb16c4682400f249998bf34eb292246c9009e17f" }, "anger_right": { "category": "symbols", "moji": "🗯", + "description": "right anger bubble", "unicodeVersion": "7.0", "digest": "8b049511ef3b1b28325841e2f87c60773eaf2f65cabba58d8b0ec3de9b10c0ae" }, "angry": { "category": "people", "moji": "😠", + "description": "angry face", "unicodeVersion": "6.0", "digest": "7e09e7e821f511606341fb5ce4011a8ed9809766ab86b7983ffa6ea352b39ec1" }, "ant": { "category": "nature", "moji": "🐜", + "description": "ant", "unicodeVersion": "6.0", "digest": "929abeaff7ba21ab71cd1ab798af7a6b611e3b3ce1af80cede09a116b223e442" }, "apple": { "category": "food", "moji": "🍎", + "description": "red apple", "unicodeVersion": "6.0", "digest": "2a1b85ce57e3d236ae7777dcf332ec37d03bfd7b19806521a353bc532083224d" }, "aquarius": { "category": "symbols", "moji": "♒", + "description": "aquarius", "unicodeVersion": "1.1", "digest": "fdc42cd41b0dace5eae6baba3143f1e40295d48a29e7103a5bba1d84a056c39d" }, "aries": { "category": "symbols", "moji": "♈", + "description": "aries", "unicodeVersion": "1.1", "digest": "deb135debcde0a98f40361a84ab64d57c18b5b445cd2f4199e8936f052899737" }, "arrow_backward": { "category": "symbols", "moji": "◀", + "description": "black left-pointing triangle", "unicodeVersion": "1.1", "digest": "e162ac82e90d1e925d479fa5c45b9340e0a53287be04e43cbbb2a89c7e7e45e4" }, "arrow_double_down": { "category": "symbols", "moji": "⏬", + "description": "black down-pointing double triangle", "unicodeVersion": "6.0", "digest": "03ca890b05338d40972c7a056d672df620a203c6ca52ff3ff530f1a710905507" }, "arrow_double_up": { "category": "symbols", "moji": "⏫", + "description": "black up-pointing double triangle", "unicodeVersion": "6.0", "digest": "e753f05bce993d62d5dc79e33c441ced059381b6ce21fa3ea4200f1b3236e59d" }, "arrow_down": { "category": "symbols", "moji": "⬇", + "description": "downwards black arrow", "unicodeVersion": "4.0", "digest": "9bf1bd2ea652ca9321087de58c7a112ea04c35676a6ee0766154183f8b95af6c" }, "arrow_down_small": { "category": "symbols", "moji": "🔽", + "description": "down-pointing small red triangle", "unicodeVersion": "6.0", "digest": "7766198bc60cf59d6cdaeeaa700c2282bfff2f0fdeb22cf4581ca284b87a3bb7" }, "arrow_forward": { "category": "symbols", "moji": "▶", + "description": "black right-pointing triangle", "unicodeVersion": "1.1", "digest": "db77d9accd1e02224f5d612f79cd691e6befdf22063475204836be6572510fb7" }, "arrow_heading_down": { "category": "symbols", "moji": "⤵", + "description": "arrow pointing rightwards then curving downwards", "unicodeVersion": "3.2", "digest": "f5396069c8f63c13e6c3e0ecd34267c932451309ade9c1171d410563153bf909" }, "arrow_heading_up": { "category": "symbols", "moji": "⤴", + "description": "arrow pointing rightwards then curving upwards", "unicodeVersion": "3.2", "digest": "1cad71923fa3df24cf543cae4ce775b0f74936f2edd685fd86a7525c41a14568" }, "arrow_left": { "category": "symbols", "moji": "⬅", + "description": "leftwards black arrow", "unicodeVersion": "4.0", "digest": "b629bb3dbe161ef89cfcfced0c7968a68e44a019ad509132987e4973bdc874e7" }, "arrow_lower_left": { "category": "symbols", "moji": "↙", + "description": "south west arrow", "unicodeVersion": "1.1", "digest": "879136ba0e24e6bf3be70118abcb716d71bd74f7b62347bc052b6533c0ea534d" }, "arrow_lower_right": { "category": "symbols", "moji": "↘", + "description": "south east arrow", "unicodeVersion": "1.1", "digest": "86d52ac9b961991e3aaa6a9f9b5ace4db6ffd1b5c171c09c23b516473b55066d" }, "arrow_right": { "category": "symbols", "moji": "➡", + "description": "black rightwards arrow", "unicodeVersion": "1.1", "digest": "45f26a1cbb0f00ed3609b39da52e9d9e896a77e361c4c8036b1bf8038171bd49" }, "arrow_right_hook": { "category": "symbols", "moji": "↪", + "description": "rightwards arrow with hook", "unicodeVersion": "1.1", "digest": "4f452679c71bcea4fc4a701c55156fef3ddc1ebbc70570bedfc9d3a029637ab1" }, "arrow_up": { "category": "symbols", "moji": "⬆", + "description": "upwards black arrow", "unicodeVersion": "4.0", "digest": "982b988ef6651d8a71867ba7c87f640f62dd0eeb0b7c358f5a5c37e8fe507b8b" }, "arrow_up_down": { "category": "symbols", "moji": "↕", + "description": "up down arrow", "unicodeVersion": "1.1", "digest": "645ed8fb6646f49bfd95af1752336deacdadbe5cba13904023a704288f3b0e2c" }, "arrow_up_small": { "category": "symbols", "moji": "🔼", + "description": "up-pointing small red triangle", "unicodeVersion": "6.0", "digest": "4a8c5789c13a852517e639e7a62c2d331464e6fb0358985aa97c1515e97b5e8b" }, "arrow_upper_left": { "category": "symbols", "moji": "↖", + "description": "north west arrow", "unicodeVersion": "1.1", "digest": "79026f828d6ceb7c55a9542770962ba6dcd08203995f6ceeb70333a12307d376" }, "arrow_upper_right": { "category": "symbols", "moji": "↗", + "description": "north east arrow", "unicodeVersion": "1.1", "digest": "7e0f33dfbe65628991c170130d366a3e2cedaf8862ddfcaf3960f395d3da1926" }, "arrows_clockwise": { "category": "symbols", "moji": "🔃", + "description": "clockwise downwards and upwards open circle arrows", "unicodeVersion": "6.0", "digest": "88669679977f7157f0acaa9d6a1b77ccf84d25eb78c5bc8afcde38d3635e7144" }, "arrows_counterclockwise": { "category": "symbols", "moji": "🔄", + "description": "anticlockwise downwards and upwards open circle ar", "unicodeVersion": "6.0", "digest": "a2c6a6d3643c128aee3304cd03bb3d7cfe4d35d3ba825bc9c1142d7832b4426e" }, "art": { "category": "activity", "moji": "🎨", + "description": "artist palette", "unicodeVersion": "6.0", "digest": "b6bc6c4bfb594aadcbb641d006031867678504764bbe0ab84e7b08567a9498da" }, "articulated_lorry": { "category": "travel", "moji": "🚛", + "description": "articulated lorry", "unicodeVersion": "6.0", "digest": "c115e6613ebd718268aa31d265e017138b9fb58bbb8201eb3f40de2380e460aa" }, "asterisk": { "category": "symbols", "moji": "*⃣", + "description": "keycap asterisk", "unicodeVersion": "3.0", "digest": "33d92093f2914448d5a939cf62e8ee3e32931923abdef5f0210e8a8150fa312d" }, "astonished": { "category": "people", "moji": "😲", + "description": "astonished face", "unicodeVersion": "6.0", "digest": "f8531bdda5070d10492709085f4ff652b8be9be6458758940358b9fc594a1f14" }, "athletic_shoe": { "category": "people", "moji": "👟", + "description": "athletic shoe", "unicodeVersion": "6.0", "digest": "1f90dc390e0dea679085465b7f9e786dfd7dd56a3b219987144ed37ab1e9bf95" }, "atm": { "category": "symbols", "moji": "🏧", + "description": "automated teller machine", "unicodeVersion": "6.0", "digest": "7d3ce6a6afb4951546883404b8e36904179f88f1aa533706cf7bf0bbe0d6fd3c" }, "atom": { "category": "symbols", "moji": "⚛", + "description": "atom symbol", "unicodeVersion": "4.1", "digest": "6b6bb83b00707a314e46ff8eefbda40978a291ec7881caba1b1ee273f49c1368" }, "avocado": { "category": "food", "moji": "🥑", + "description": "avocado", "unicodeVersion": "9.0", "digest": "bc1fb203d63b18985598400925de24050bb192afda1cbf0813f85cb139869eff" }, "b": { "category": "symbols", "moji": "🅱", + "description": "negative squared latin capital letter b", "unicodeVersion": "6.0", "digest": "722f9db9442e7c0fc0d0ac0f5291fbf47c6a0ac4d8abd42e97957da705fb82bf" }, "baby": { "category": "people", "moji": "👶", + "description": "baby", "unicodeVersion": "6.0", "digest": "219ae5a571aaf90c060956cd1c56dcc27708c827cecdca3ba1122058a3c4847b" }, "baby_bottle": { "category": "food", "moji": "🍼", + "description": "baby bottle", "unicodeVersion": "6.0", "digest": "4fb71689e9d634e8d1699cf454a71e43f2b5b1a5dbab0bf186626934fdf5b782" }, "baby_chick": { "category": "nature", "moji": "🐤", + "description": "baby chick", "unicodeVersion": "6.0", "digest": "14119874e9b5548028dfb9cc593a541efc1d075ac839a565b92e0c3253cffe7e" }, "baby_symbol": { "category": "symbols", "moji": "🚼", + "description": "baby symbol", "unicodeVersion": "6.0", "digest": "fb4db66868cda45ea3879ffc2ff4f763c56d2d889ae0ab17fe171129ede02f98" }, "baby_tone1": { "category": "people", "moji": "👶🏻", + "description": "baby tone 1", "unicodeVersion": "8.0", "digest": "cd3faf223a298c34e05d469d9d0db08438d97df7fd82c0973f8a9e07d553f5b1" }, "baby_tone2": { "category": "people", "moji": "👶🏼", + "description": "baby tone 2", "unicodeVersion": "8.0", "digest": "5b4539e22e0dd726c27eb8af2357f9240a52aed3f710f3234571cff029cc6198" }, "baby_tone3": { "category": "people", "moji": "👶🏽", + "description": "baby tone 3", "unicodeVersion": "8.0", "digest": "720e740e1ac63c6372269132b1fb6e07a6b91f5c808cc3adef59f0b4500e5e72" }, "baby_tone4": { "category": "people", "moji": "👶🏾", + "description": "baby tone 4", "unicodeVersion": "8.0", "digest": "5e43b69c509bd526ad6f081764578c30b6f3285fb7442222e05ccf62e53bfb64" }, "baby_tone5": { "category": "people", "moji": "👶🏿", + "description": "baby tone 5", "unicodeVersion": "8.0", "digest": "85bba6e0940ccfb99999fe124e815f9dd340d00a5568e13967b02245a62dbf54" }, "back": { "category": "symbols", "moji": "🔙", + "description": "back with leftwards arrow above", "unicodeVersion": "6.0", "digest": "083e4e48b51092c28efb4532e840e1091b5d4b685c6e0f221aa0228f061cd91e" }, "bacon": { "category": "food", "moji": "🥓", + "description": "bacon", "unicodeVersion": "9.0", "digest": "18ad3817f1f88a69706db5727a58e763dde6c21a2d4f184c3d728c32dc5fa05a" }, "badminton": { "category": "activity", "moji": "🏸", + "description": "badminton racquet", "unicodeVersion": "8.0", "digest": "353eb7ee93decd9fe0072e4d78a5618d5e2d9e77a6e4de9fe171870d75e02a66" }, "baggage_claim": { "category": "symbols", "moji": "🛄", + "description": "baggage claim", "unicodeVersion": "6.0", "digest": "7d6bceca92c266da6d2b91dfcf244546fc11022e039e7da8e6888c1696bb2186" }, "balloon": { "category": "objects", "moji": "🎈", + "description": "balloon", "unicodeVersion": "6.0", "digest": "65760aedc1503b426927cff78c24449d563843a274961d962718fa9638375d54" }, "ballot_box": { "category": "objects", "moji": "🗳", + "description": "ballot box with ballot", "unicodeVersion": "7.0", "digest": "4175a56eca5c6458574a681e109b1403fbb143cf27f69ae6c1917650f3e08892" }, "ballot_box_with_check": { "category": "symbols", "moji": "☑", + "description": "ballot box with check", "unicodeVersion": "1.1", "digest": "c98d6f3588dd87e2f318bbfe6c646399a905450edfd814edae4e5b1bddef2134" }, "bamboo": { "category": "nature", "moji": "🎍", + "description": "pine decoration", "unicodeVersion": "6.0", "digest": "e4ee65088df43d7081b1ce6fd996f66f3e0accd88840855c47a98a22997823dd" }, "banana": { "category": "food", "moji": "🍌", + "description": "banana", "unicodeVersion": "6.0", "digest": "f9e8ff910c282c20a8907ff64926b5de4ee250529a1ed718fb33302e6fff8dd9" }, "bangbang": { "category": "symbols", "moji": "‼", + "description": "double exclamation mark", "unicodeVersion": "1.1", "digest": "76536fee63fe964a3f3839d309b1f45028fb0c43f4d1eeee495f17e1532b4def" }, "bank": { "category": "travel", "moji": "🏦", + "description": "bank", "unicodeVersion": "6.0", "digest": "f5d2976bf6d521638ccacc74be06bd4abfeab06c5d898a9d245edad45a5b6306" }, "bar_chart": { "category": "objects", "moji": "📊", + "description": "bar chart", "unicodeVersion": "6.0", "digest": "65a328a1b2d7a5332dd4d93f4dbca13d976f0a505b00835c3fc458e394804240" }, "barber": { "category": "objects", "moji": "💈", + "description": "barber pole", "unicodeVersion": "6.0", "digest": "5e8053d3bb3765a8632fd1cbfe21163f74ed79f6be377eb9603eaaf883d8dc46" }, "baseball": { "category": "activity", "moji": "⚾", + "description": "baseball", "unicodeVersion": "5.2", "digest": "46ac16f8b5455b942f6dbff9483a6fd277721e6719d2731573baabd21c44b34f" }, "basketball": { "category": "activity", "moji": "🏀", + "description": "basketball and hoop", "unicodeVersion": "6.0", "digest": "cc83e2aea8fcd2e9a5789e1932ee3766c40843c142fd3565c4e77dafb21ec7d7" }, "basketball_player": { "category": "activity", "moji": "⛹", + "description": "person with ball", "unicodeVersion": "5.2", "digest": "793ba53c95e8def769383b612037bc9b9bceecaf1e0430c50a4cc128ad18d9b9" }, "basketball_player_tone1": { "category": "activity", "moji": "⛹🏻", + "description": "person with ball tone 1", "unicodeVersion": "8.0", "digest": "2a06522b971e68ee5b8777a58253009b548f4da2fb723c638acb3d7b04edba8f" }, "basketball_player_tone2": { "category": "activity", "moji": "⛹🏼", + "description": "person with ball tone 2", "unicodeVersion": "8.0", "digest": "ecc0e44ab9bc478ba45a055fd69a3a38377b917aac5047963fe80ff8ae5fd8e3" }, "basketball_player_tone3": { "category": "activity", "moji": "⛹🏽", + "description": "person with ball tone 3", "unicodeVersion": "8.0", "digest": "2d38f1851c685d29532c042461d7b5b996e5f04f0ed54857c66073c62a99ceac" }, "basketball_player_tone4": { "category": "activity", "moji": "⛹🏾", + "description": "person with ball tone 4", "unicodeVersion": "8.0", "digest": "09e957c6e9ffc196415f28073aa261feba8efba0bdc694dc08f8f7cd1f88f720" }, "basketball_player_tone5": { "category": "activity", "moji": "⛹🏿", + "description": "person with ball tone 5", "unicodeVersion": "8.0", "digest": "c631cefc5d2a0a31bdb9f0a0d97ea68b1c6928e565468998403034644572a0b0" }, "bat": { "category": "nature", "moji": "🦇", + "description": "bat", "unicodeVersion": "9.0", "digest": "8fc19e0d7d6f80906bdbc06d616a810de66180d96cf28070a53fa61b88904535" }, "bath": { "category": "activity", "moji": "🛀", + "description": "bath", "unicodeVersion": "6.0", "digest": "33b371832f90aad50baf5296f3ad4cc081c319b279f989c74409903d8568e917" }, "bath_tone1": { "category": "activity", "moji": "🛀🏻", + "description": "bath tone 1", "unicodeVersion": "8.0", "digest": "7ae2989e47788ba71359d52da68feec95aaff68a77d5a6556957df1617af8536" }, "bath_tone2": { "category": "activity", "moji": "🛀🏼", + "description": "bath tone 2", "unicodeVersion": "8.0", "digest": "2e86f8edad54d15a7094cd52160cbe51d10aa1750cfb0b3b58e93533f070e327" }, "bath_tone3": { "category": "activity", "moji": "🛀🏽", + "description": "bath tone 3", "unicodeVersion": "8.0", "digest": "654c0cd083a67ff330a38d07352876d265390e5399e5352598d64a6c7e5eeba7" }, "bath_tone4": { "category": "activity", "moji": "🛀🏾", + "description": "bath tone 4", "unicodeVersion": "8.0", "digest": "adad88c6830f31c4b5be194d1987d6aadf4adf45e4cb7f2e4657f0d20c0d663a" }, "bath_tone5": { "category": "activity", "moji": "🛀🏿", + "description": "bath tone 5", "unicodeVersion": "8.0", "digest": "952c4c9bf24e001e23a33ebf97bd92969cd9143e28ce93f9aafc708a8f966903" }, "bathtub": { "category": "objects", "moji": "🛁", + "description": "bathtub", "unicodeVersion": "6.0", "digest": "844dffb87ef872594195069b0d0df27c3fe51f3967ccbc8b2df811a086dd483a" }, "battery": { "category": "objects", "moji": "🔋", + "description": "battery", "unicodeVersion": "6.0", "digest": "949ae06648667fb13d9121a6dfdd03bf8692794b28c36e9a8e8ac4515664449a" }, "beach": { "category": "travel", "moji": "🏖", + "description": "beach with umbrella", "unicodeVersion": "7.0", "digest": "37fa2158977d470186caaa1aa06669b6dc5026ba49a0c44c5255541f8e974e26" }, "beach_umbrella": { "category": "objects", "moji": "⛱", + "description": "umbrella on ground", "unicodeVersion": "5.2", "digest": "d045f1de10038b9fb1eaa2529b2f80b7e3be1cff503efcc2d680663d1fbbc18f" }, "bear": { "category": "nature", "moji": "🐻", + "description": "bear face", "unicodeVersion": "6.0", "digest": "a4b9066eaa5681e6af06e596a96a5217037460ffc3b013e8db4d34d762413246" }, "bed": { "category": "objects", "moji": "🛏", + "description": "bed", "unicodeVersion": "7.0", "digest": "08f6e20db51b1fb650b390a0a3074938646772f3fcee8c295d47742e44fe1e30" }, "bee": { "category": "nature", "moji": "🐝", + "description": "honeybee", "unicodeVersion": "6.0", "digest": "5beb9a1650681b4adf69999d4808231c38f41a3ec693480b807cda86f964c570" }, "beer": { "category": "food", "moji": "🍺", + "description": "beer mug", "unicodeVersion": "6.0", "digest": "69e227104976548ee0f37375fe1526fd65ef0a328d2d92db2feb1edfd7032bd4" }, "beers": { "category": "food", "moji": "🍻", + "description": "clinking beer mugs", "unicodeVersion": "6.0", "digest": "db8b32d93bf6d161a3b027e55651d8f51231b13928b3610987ef62bb634d7501" }, "beetle": { "category": "nature", "moji": "🐞", + "description": "lady beetle", "unicodeVersion": "6.0", "digest": "5aaa428e3f63f7cd1696839ab05be03fa0cd0cbed30a05c36cb270da330c3849" }, "beginner": { "category": "symbols", "moji": "🔰", + "description": "japanese symbol for beginner", "unicodeVersion": "6.0", "digest": "2de4fdf92f182c42b12b7527034eaf767d996848b61f31ee69167728411ca0b1" }, "bell": { "category": "symbols", "moji": "🔔", + "description": "bell", "unicodeVersion": "6.0", "digest": "18d419417746ead408072b78fe2edb6314cdb49492873966fa9f9f06be09899b" }, "bellhop": { "category": "objects", "moji": "🛎", + "description": "bellhop bell", "unicodeVersion": "7.0", "digest": "b8187bc4059f6a0924a47fe3f6c07f656bed0334bbcbfa1e89f800fe6594ff08" }, "bento": { "category": "food", "moji": "🍱", + "description": "bento box", "unicodeVersion": "6.0", "digest": "d46d4f681c5da7f7678b51be3445454a8ed18d917e132ae79077f05310e485f1" }, "bicyclist": { "category": "activity", "moji": "🚴", + "description": "bicyclist", "unicodeVersion": "6.0", "digest": "3302147b6b47c16adb97d78b7b761a1ca80e6d0b41d0b60f4da338d2f55f968b" }, "bicyclist_tone1": { "category": "activity", "moji": "🚴🏻", + "description": "bicyclist tone 1", "unicodeVersion": "8.0", "digest": "27eaae0eb61f5e7b3cd9faf02c042d6643a368051a7c9d7da4e0fb9802d39242" }, "bicyclist_tone2": { "category": "activity", "moji": "🚴🏼", + "description": "bicyclist tone 2", "unicodeVersion": "8.0", "digest": "39ee9e1071700da7079ad0146bf5711c3a222991eeca8b29b72a65677604444d" }, "bicyclist_tone3": { "category": "activity", "moji": "🚴🏽", + "description": "bicyclist tone 3", "unicodeVersion": "8.0", "digest": "03e1d2c4232c896147a9d4bf43becd61edbb5c84fc7193ecea474c0f9fb36817" }, "bicyclist_tone4": { "category": "activity", "moji": "🚴🏾", + "description": "bicyclist tone 4", "unicodeVersion": "8.0", "digest": "61393d9c4805be0379d86dd5bec9a1b02314433ab36cfd85bb48dfd073746617" }, "bicyclist_tone5": { "category": "activity", "moji": "🚴🏿", + "description": "bicyclist tone 5", "unicodeVersion": "8.0", "digest": "2b46d5f8303e5710dbf5db3a4edc9d88a032fe123fe79158024c9f51df5458c6" }, "bike": { "category": "travel", "moji": "🚲", + "description": "bicycle", "unicodeVersion": "6.0", "digest": "b41daa7c549d483e2336186a28baaa8ecb11986f490c0c54c793c44900c8f652" }, "bikini": { "category": "people", "moji": "👙", + "description": "bikini", "unicodeVersion": "6.0", "digest": "07fe156f64673818d69ce3bf03950ca59e3b5d346e45ca541da4078ab791f5ae" }, "biohazard": { "category": "symbols", "moji": "☣", + "description": "biohazard sign", "unicodeVersion": "1.1", "digest": "96163e31f0b8dc5a59772133ede9cc2f40f94330d0b15e3d044b28747e2be788" }, "bird": { "category": "nature", "moji": "🐦", + "description": "bird", "unicodeVersion": "6.0", "digest": "f916eaf8f271b3767ade9eabb69594c0479f45472d471cabaf59f6e965c161e0" }, "birthday": { "category": "food", "moji": "🎂", + "description": "birthday cake", "unicodeVersion": "6.0", "digest": "89e7c4c598ebee8ec8ab11ebe4ccc6defb7c4d2987ee2379a19b3b59827dd98a" }, "black_circle": { "category": "symbols", "moji": "⚫", + "description": "medium black circle", "unicodeVersion": "4.1", "digest": "c2ba672994ad0f99d7fdc449f3fee45a2dca68a58f9fe95825b38465a30ef44e" }, "black_heart": { "category": "symbols", "moji": "🖤", + "description": "black heart", "unicodeVersion": "9.0", "digest": "f334679168d6dd7328c28e9ae3cb2b1fca0e9c2777938d586bfe623db2a688b9" }, "black_joker": { "category": "symbols", "moji": "🃏", + "description": "playing card black joker", "unicodeVersion": "6.0", "digest": "d004b25f186494d5b2c65204caa9daecd749c840a0bea5718735e18109e5394d" }, "black_large_square": { "category": "symbols", "moji": "⬛", + "description": "black large square", "unicodeVersion": "5.1", "digest": "cbd90dcbc2f674eafa53820548b5263c18c9845ab39937f085e85aca0aebb479" }, "black_medium_small_square": { "category": "symbols", "moji": "◾", + "description": "black medium small square", "unicodeVersion": "3.2", "digest": "ab38363c2e862b8f67c719397a09a18e1ef996eec190691fdf769f5cfb209660" }, "black_medium_square": { "category": "symbols", "moji": "◼", + "description": "black medium square", "unicodeVersion": "3.2", "digest": "c9ffa87c37e8ee65fadcf755176949901aec7367e02abb85e63cad60cd922116" }, "black_nib": { "category": "objects", "moji": "✒", + "description": "black nib", "unicodeVersion": "1.1", "digest": "58fb23b1155102970eaa23765e7d529a21e8e545e076ec1158bf11b4de5f51a8" }, "black_small_square": { "category": "symbols", "moji": "▪", + "description": "black small square", "unicodeVersion": "1.1", "digest": "f69be6de578fffce5a3e60eda690104b2ef6a855c630040104fb760a02ff1aef" }, "black_square_button": { "category": "symbols", "moji": "🔲", + "description": "black square button", "unicodeVersion": "6.0", "digest": "9d818fcd08ed38cd0bbbcfd83e665aa29b3761c0d8b9806d8954d36785e267a8" }, "blossom": { "category": "nature", "moji": "🌼", + "description": "blossom", "unicodeVersion": "6.0", "digest": "e8cf369d4e4cdb4eccc2ebcbb35439b0344221115701daae642e58dff8544922" }, "blowfish": { "category": "nature", "moji": "🐡", + "description": "blowfish", "unicodeVersion": "6.0", "digest": "e706849ed00f08a82312381c76f6f9ba6cc261fbf87a839c85e7dd54138f9dc3" }, "blue_book": { "category": "objects", "moji": "📘", + "description": "blue book", "unicodeVersion": "6.0", "digest": "4c845748fe890516b32981b0b62bf3e8e9d906840c2060179f4f844100780615" }, "blue_car": { "category": "travel", "moji": "🚙", + "description": "recreational vehicle", "unicodeVersion": "6.0", "digest": "eca91934eb5481726cfd897b1ed5eac306e14d02499fbe49316aaec6c72b6707" }, "blue_heart": { "category": "symbols", "moji": "💙", + "description": "blue heart", "unicodeVersion": "6.0", "digest": "2caa0c8d18538cc871c6fe328a52f71e1df8aabf4d1cc2f5324b261d1b8cb99a" }, "blush": { "category": "people", "moji": "😊", + "description": "smiling face with smiling eyes", "unicodeVersion": "6.0", "digest": "3bfe8d603cfa39999c164779f666d39bbc507f124ba80233ee72da7b3b0c0457" }, "boar": { "category": "nature", "moji": "🐗", + "description": "boar", "unicodeVersion": "6.0", "digest": "c9d67479cace427ac3c30460fcffa1bf9a8e5262c0390962405dbbe6bf830fa6" }, "bomb": { "category": "objects", "moji": "💣", + "description": "bomb", "unicodeVersion": "6.0", "digest": "0155559abc4084f80e9b0b2a2091b8710ddd6369993b7fdd0685f4f8c2fd7e6c" }, "book": { "category": "objects", "moji": "📖", + "description": "open book", "unicodeVersion": "6.0", "digest": "9d912a9d1bb10dc7f2645b345ed09e90461e83df0de275acb806f1f75cef1fcf" }, "bookmark": { "category": "objects", "moji": "🔖", + "description": "bookmark", "unicodeVersion": "6.0", "digest": "5705e3108259d6900649157843c50e22d0086c3630b291d3f942da1a736e3e3d" }, "bookmark_tabs": { "category": "objects", "moji": "📑", + "description": "bookmark tabs", "unicodeVersion": "6.0", "digest": "c8fc7c9f3f82e1ccc97fc591345fdd88b09eec0fca428d8d4632a121cf1bc39a" }, "books": { "category": "objects", "moji": "📚", + "description": "books", "unicodeVersion": "6.0", "digest": "cbcf55d39dd05d26ef7350bc51e0e2f064f78bb8f59d407b516d63f68558f8e4" }, "boom": { "category": "nature", "moji": "💥", + "description": "collision symbol", "unicodeVersion": "6.0", "digest": "f5400e9583f7f997cd2385f21379f6229424a9b221445bc8f36c0bb64bdb3168" }, "boot": { "category": "people", "moji": "👢", + "description": "womans boots", "unicodeVersion": "6.0", "digest": "b4706ff35909a6fb759a3b8a797e90cb67ffc60e4853386a7d89ace9693a9364" }, "bouquet": { "category": "nature", "moji": "💐", + "description": "bouquet", "unicodeVersion": "6.0", "digest": "b93751a27b40f6185a22b3e8b413f0fe09b6010d1057c672e1a23088e0b8286f" }, "bow": { "category": "people", "moji": "🙇", + "description": "person bowing deeply", "unicodeVersion": "6.0", "digest": "33cd6da4d408f18d98bebc6a277dea8b914150e32ee472586ce3f1eb814462bd" }, "bow_and_arrow": { "category": "activity", "moji": "🏹", + "description": "bow and arrow", "unicodeVersion": "8.0", "digest": "051b4d50ab21a68b8583a6313ec183e3e1e96f493b0f4541fbb888f0b95fdd4d" }, "bow_tone1": { "category": "people", "moji": "🙇🏻", + "description": "person bowing deeply tone 1", "unicodeVersion": "8.0", "digest": "995c8400ad60d5adc66c9ae5e3c0ecf56c48b478ad79418d45b6289933d25bdd" }, "bow_tone2": { "category": "people", "moji": "🙇🏼", + "description": "person bowing deeply tone 2", "unicodeVersion": "8.0", "digest": "af89eec2fccda99d9bdd373b2345595882fee1c0a15d29af9028089e20255325" }, "bow_tone3": { "category": "people", "moji": "🙇🏽", + "description": "person bowing deeply tone 3", "unicodeVersion": "8.0", "digest": "015d8122abdf2d0caa03815545f50fb7a71e05dacd46aaa133cc9ace5192f266" }, "bow_tone4": { "category": "people", "moji": "🙇🏾", + "description": "person bowing deeply tone 4", "unicodeVersion": "8.0", "digest": "e8409096a795b775def654d36aeccb8eb91e83d7d1b32145cd73fd0b7b9e885c" }, "bow_tone5": { "category": "people", "moji": "🙇🏿", + "description": "person bowing deeply tone 5", "unicodeVersion": "8.0", "digest": "d87042cde8dbad9fb1a91a2ec60116e27b4a76388b5779d771a0bbae12a2814d" }, "bowling": { "category": "activity", "moji": "🎳", + "description": "bowling", "unicodeVersion": "6.0", "digest": "737f2cdfa4ac964baade585a39771b18080bd5e9b55c8661d3518f468f344662" }, "boxing_glove": { "category": "activity", "moji": "🥊", + "description": "boxing glove", "unicodeVersion": "9.0", "digest": "c914b2ce45f20afad66ad6f0d1b0750c4469e4f48b686dfc4aad1ec8d289c563" }, "boy": { "category": "people", "moji": "👦", + "description": "boy", "unicodeVersion": "6.0", "digest": "7bc0173d8c88f3f12d41f213f7a3a9f5ebf65efad610fd5a2a31935128a6a6c1" }, "boy_tone1": { "category": "people", "moji": "👦🏻", + "description": "boy tone 1", "unicodeVersion": "8.0", "digest": "c0e2f0483715b239fe145b0056566f7a3a722319d9a87c1e66733dff1916a19f" }, "boy_tone2": { "category": "people", "moji": "👦🏼", + "description": "boy tone 2", "unicodeVersion": "8.0", "digest": "0001d0bd1ff4dbd898604ba965b4039d09667d955bc0349301b992f9ab6dd7fd" }, "boy_tone3": { "category": "people", "moji": "👦🏽", + "description": "boy tone 3", "unicodeVersion": "8.0", "digest": "e0f08755955fd2e0bd1c5d5e84429b2a234b24a744bb50bb9f1148495b2b29f9" }, "boy_tone4": { "category": "people", "moji": "👦🏾", + "description": "boy tone 4", "unicodeVersion": "8.0", "digest": "04b6bfee58a26b1ce2e5b403504a7033aaf395f03f5cd23e824f32c90c395fe6" }, "boy_tone5": { "category": "people", "moji": "👦🏿", + "description": "boy tone 5", "unicodeVersion": "8.0", "digest": "0f76e97237203950da36c737dcc6f56dcd6c123401a8c817a0636376c7f38ef5" }, "bread": { "category": "food", "moji": "🍞", + "description": "bread", "unicodeVersion": "6.0", "digest": "81739830f16f33e6a1dd7cc17c25df207846062bb5167bb8abed7fdd49268b86" }, "bride_with_veil": { "category": "people", "moji": "👰", + "description": "bride with veil", "unicodeVersion": "6.0", "digest": "8e24bd91c3f564cf6148f2b3b4a7d692c11dd059e76a13331fdfb04ae060ea70" }, "bride_with_veil_tone1": { "category": "people", "moji": "👰🏻", + "description": "bride with veil tone 1", "unicodeVersion": "8.0", "digest": "0bd2f16f72586f50e768b14b9b353f2e98ccbb2581a568c33b06be56e70ca063" }, "bride_with_veil_tone2": { "category": "people", "moji": "👰🏼", + "description": "bride with veil tone 2", "unicodeVersion": "8.0", "digest": "e5463f811b2075754f0718b891757cd2e81071edf7af2215581227e1aad1d068" }, "bride_with_veil_tone3": { "category": "people", "moji": "👰🏽", + "description": "bride with veil tone 3", "unicodeVersion": "8.0", "digest": "e5a053a26f7ccebae7eb12f638be5ed80f77b744708d783eab2eb8aa091cf516" }, "bride_with_veil_tone4": { "category": "people", "moji": "👰🏾", + "description": "bride with veil tone 4", "unicodeVersion": "8.0", "digest": "410e23825e4401460946dc67a618bd3ace6e1a7c07dd88580a2349423685261f" }, "bride_with_veil_tone5": { "category": "people", "moji": "👰🏿", + "description": "bride with veil tone 5", "unicodeVersion": "8.0", "digest": "454e87e5a74e13e5b4993541231516fbbe6dbe9f990e1a6f3f4a744d7d4c1615" }, "bridge_at_night": { "category": "travel", "moji": "🌉", + "description": "bridge at night", "unicodeVersion": "6.0", "digest": "9d3cda5a59e27e3c90939f1ddbe7e998b3ea4fcacfa1467dea0edf39613c2d7f" }, "briefcase": { "category": "people", "moji": "💼", + "description": "briefcase", "unicodeVersion": "6.0", "digest": "9d00d6a92632aaadc71b017f448c883b27eb31a7554ebb51f7e3a9841f0f7f2b" }, "broken_heart": { "category": "symbols", "moji": "💔", + "description": "broken heart", "unicodeVersion": "6.0", "digest": "c7ca53f444d72e596af46b61ffbc9e7c18a645020c22691e44f967db98dbf853" }, "bug": { "category": "nature", "moji": "🐛", + "description": "bug", "unicodeVersion": "6.0", "digest": "0dccb1d5eb91769377b4c5b310f007b60f54a5c48ba9e467b3a06898a4831b90" }, "bulb": { "category": "objects", "moji": "💡", + "description": "electric light bulb", "unicodeVersion": "6.0", "digest": "ccdaa2dfde5a88a347035a94b9d4d86cfc335ce0a73292423f5788a4bd21a5a8" }, "bullettrain_front": { "category": "travel", "moji": "🚅", + "description": "high-speed train with bullet nose", "unicodeVersion": "6.0", "digest": "5195a6a6d23f28e1aa5ebac6ede0f6c6a8b7ff33a9edf034814f227fe976177a" }, "bullettrain_side": { "category": "travel", "moji": "🚄", + "description": "high-speed train", "unicodeVersion": "6.0", "digest": "96e74842e919716b7bbbab57339bfd70f099a9bcb4710dffd7c80cf38a7bbff7" }, "burrito": { "category": "food", "moji": "🌯", + "description": "burrito", "unicodeVersion": "8.0", "digest": "b2cf81f1efdf87e674461f73f67cd4b58a5f695e65598d0dd3899f2597da43cf" }, "bus": { "category": "travel", "moji": "🚌", + "description": "bus", "unicodeVersion": "6.0", "digest": "192850b762edad21ac8770df38b9cae6d2bc1697a838462f3e36066bfb4eee50" }, "busstop": { "category": "travel", "moji": "🚏", + "description": "bus stop", "unicodeVersion": "6.0", "digest": "adabb1ec36402b33feb636eae3656e5a8b51ff1071bcb14125d8ab80d6d12d2a" }, "bust_in_silhouette": { "category": "people", "moji": "👤", + "description": "bust in silhouette", "unicodeVersion": "6.0", "digest": "277ae43301f1e49e0be03c8e52f0dc7b70c67f9d146bca0a14172e0098f115e6" }, "busts_in_silhouette": { "category": "people", "moji": "👥", + "description": "busts in silhouette", "unicodeVersion": "6.0", "digest": "7fee96f1b68bb2c6002e47f2ed13c06baa6a3168441b9aca572db7ec45612f7b" }, "butterfly": { "category": "nature", "moji": "🦋", + "description": "butterfly", "unicodeVersion": "9.0", "digest": "a91b6598c17b44a8dc8935a1d99e25f4483ea41470cdd2da343039a9eec29ef1" }, "cactus": { "category": "nature", "moji": "🌵", + "description": "cactus", "unicodeVersion": "6.0", "digest": "2c5c4c35f26c7046fdc002b337e0d939729b33a26980e675950f9934c91e40fd" }, "cake": { "category": "food", "moji": "🍰", + "description": "shortcake", "unicodeVersion": "6.0", "digest": "b928902df8084210d51c1da36f9119164a325393c391b28cd8ea914e0b95c17b" }, "calendar": { "category": "objects", "moji": "📆", + "description": "tear-off calendar", "unicodeVersion": "6.0", "digest": "9d990be27778daab041a3583edbd8f83fc8957e42a3aec729c0e2e224a8d05e3" }, "calendar_spiral": { "category": "objects", "moji": "🗓", + "description": "spiral calendar pad", "unicodeVersion": "7.0", "digest": "441a0750eade7ce33e28e58bec76958990c412b68409fcdde59ebad1f25361bb" }, "call_me": { "category": "people", "moji": "🤙", + "description": "call me hand", "unicodeVersion": "9.0", "digest": "83d2ed96dcb8b4adf4f4d030ffd07e25ca16351e1a4fbefdf9f46f5ca496a55f" }, "call_me_tone1": { "category": "people", "moji": "🤙🏻", + "description": "call me hand tone 1", "unicodeVersion": "9.0", "digest": "4a5748efa83e7294e8338b8795d4d315ff1cd31ead6759004d0eb330e50de8cd" }, "call_me_tone2": { "category": "people", "moji": "🤙🏼", + "description": "call me hand tone 2", "unicodeVersion": "9.0", "digest": "54feaa6e3c5789ae6e15622127f0e0213234b4b886e1588ce95814348b1f1519" }, "call_me_tone3": { "category": "people", "moji": "🤙🏽", + "description": "call me hand tone 3", "unicodeVersion": "9.0", "digest": "57e949b951e14843b712dab5a828f915ee255f5bb973db33946aab4057427419" }, "call_me_tone4": { "category": "people", "moji": "🤙🏾", + "description": "call me hand tone 4", "unicodeVersion": "9.0", "digest": "f7787e933978a09c7b8ab8d3b1e1ab395aaae998c455e93bb3db24a4c8a60fe0" }, "call_me_tone5": { "category": "people", "moji": "🤙🏿", + "description": "call me hand tone 5", "unicodeVersion": "9.0", "digest": "1fdb7d833d000b117d20d48142d3026a61cc9c8b712ebb498fa66bf75c74d7a5" }, "calling": { "category": "objects", "moji": "📲", + "description": "mobile phone with rightwards arrow at left", "unicodeVersion": "6.0", "digest": "acf668c75c11c36686005788266524a972fa1c5bcf666ff3403d909edc5cee91" }, "camel": { "category": "nature", "moji": "🐫", + "description": "bactrian camel", "unicodeVersion": "6.0", "digest": "5f927927a7ab1277d0dc8b8211436957968b1e11365a8bf535e9bb94f92c5631" }, "camera": { "category": "objects", "moji": "📷", + "description": "camera", "unicodeVersion": "6.0", "digest": "fde03e396822a36cd6ae756ede885b945a074395264162731ca5db47a3b39d80" }, "camera_with_flash": { "category": "objects", "moji": "📸", + "description": "camera with flash", "unicodeVersion": "7.0", "digest": "9afd380208187780f00244c45d4db6c5ea1ea088d4a1bd8fc92a8f3877149750" }, "camping": { "category": "travel", "moji": "🏕", + "description": "camping", "unicodeVersion": "7.0", "digest": "a42a4ff9521affa72db7b0f01da169b4cb6afb9db1c5dfad47dd4c507bfc30d9" }, "cancer": { "category": "symbols", "moji": "♋", + "description": "cancer", "unicodeVersion": "1.1", "digest": "528c6f21df99a756b553d93a7f395b0f662b30a323affd05f0cedee8ff7b41d6" }, "candle": { "category": "objects", "moji": "🕯", + "description": "candle", "unicodeVersion": "7.0", "digest": "211c04dc3a91b071c284d4180ed09f9d3320e3fd6ba8a9fddd0677bc97fd12cb" }, "candy": { "category": "food", "moji": "🍬", + "description": "candy", "unicodeVersion": "6.0", "digest": "9cff4538918f60f770fceb96e964f5dc3ce31fd08ddd2ab3bfdf2981bfa74100" }, "canoe": { "category": "travel", "moji": "🛶", + "description": "canoe", "unicodeVersion": "9.0", "digest": "56ca308cc2ad4827468cf58c4ccf6ef6b3382835a91e935540a2b973e01d2572" }, "capital_abcd": { "category": "symbols", "moji": "🔠", + "description": "input symbol for latin capital letters", "unicodeVersion": "6.0", "digest": "a416d0b3f564037b680f801fb773b6eaf67225e2cbbfd2cb8a5db0de044321fa" }, "capricorn": { "category": "symbols", "moji": "♑", + "description": "capricorn", "unicodeVersion": "1.1", "digest": "f11abad102603737b55486fe2ea4d01f28b203394bcd84f19a7948156e6c4b96" }, "card_box": { "category": "objects", "moji": "🗃", + "description": "card file box", "unicodeVersion": "7.0", "digest": "7a6199d562f30e02ed31094de6aebeb99eae8ac156f6910463dfed73256f4c9a" }, "card_index": { "category": "objects", "moji": "📇", + "description": "card index", "unicodeVersion": "6.0", "digest": "86e187e0a72ca5d00207d6ef34d66ce15046848a831c2b5184fb840c5332a2a8" }, "carousel_horse": { "category": "travel", "moji": "🎠", + "description": "carousel horse", "unicodeVersion": "6.0", "digest": "c0e7059efc39a64233f774c02ddb1ab51888fff180f906ce13a6e4f9509672fe" }, "carrot": { "category": "food", "moji": "🥕", + "description": "carrot", "unicodeVersion": "9.0", "digest": "3a6fd98b63ee73d982a9cdacb08cf7b4014368cde8ffce6056b7df25a5a472b1" }, "cartwheel": { "category": "activity", "moji": "🤸", + "description": "person doing cartwheel", "unicodeVersion": "9.0", "digest": "d78de3435e0b04a9b1a1048ae12e63e3248f9ace3a0db4d3bda584f22af18863" }, "cartwheel_tone1": { "category": "activity", "moji": "🤸🏻", + "description": "person doing cartwheel tone 1", "unicodeVersion": "9.0", "digest": "39a49781a269bb40d8efc8fd73c973b00fb2e192850ea6073062b5dea0cd5b74" }, "cartwheel_tone2": { "category": "activity", "moji": "🤸🏼", + "description": "person doing cartwheel tone 2", "unicodeVersion": "9.0", "digest": "6231eb35be45457fd648f8f4b79983f03705c9d983a18067f7e6d9ae47bc1958" }, "cartwheel_tone3": { "category": "activity", "moji": "🤸🏽", + "description": "person doing cartwheel tone 3", "unicodeVersion": "9.0", "digest": "ca483c78cc823811a8c279c501d9b283e4c990dafc5995ad40e68ecb0af554df" }, "cartwheel_tone4": { "category": "activity", "moji": "🤸🏾,", + "description": "person doing cartwheel tone 4", "unicodeVersion": "9.0", "digest": "8253afb672431c84e498014c30babb00b9284bec773009e79f7f06aa7108643e" }, "cartwheel_tone5": { "category": "activity", "moji": "🤸🏿", + "description": "person doing cartwheel tone 5", "unicodeVersion": "9.0", "digest": "6fd92baff57c38b3adb6753d9e7e547e762971a8872fd3f1e71c6aaf0b1d3ab9" }, "cat": { "category": "nature", "moji": "🐱", + "description": "cat face", "unicodeVersion": "6.0", "digest": "e52d0d3a205a0ba99094717e171a7f572b713a0e21b276ffa4a826596fe5cafc" }, "cat2": { "category": "nature", "moji": "🐈", + "description": "cat", "unicodeVersion": "6.0", "digest": "46aa67a99f782935932c77b8de93287142297abe52928c173191cf55bb8f4339" }, "cd": { "category": "objects", "moji": "💿", + "description": "optical disc", "unicodeVersion": "6.0", "digest": "16363d8a34b873c12df6354b99f575cae3d80e0d27100ed7eea70f0310953c7b" }, "chains": { "category": "objects", "moji": "⛓", + "description": "chains", "unicodeVersion": "5.2", "digest": "3884cdbc6f2b433062af06f942552e563231c24727a2f10fa280b3bb7aa614e2" }, "champagne": { "category": "food", "moji": "🍾", + "description": "bottle with popping cork", "unicodeVersion": "8.0", "digest": "9e6e8987f30a37ae0f3d7dab2f5eeb50aa32b4f31402b29315eb2994afc72457" }, "champagne_glass": { "category": "food", "moji": "🥂", + "description": "clinking glasses", "unicodeVersion": "9.0", "digest": "5a2e4773f7eb126a00122cbfa4dc535da51ce00e0bf0d8d6ff8bab8b3365f8d2" }, "chart": { "category": "symbols", "moji": "💹", + "description": "chart with upwards trend and yen sign", "unicodeVersion": "6.0", "digest": "a092dbc08f925b028286b2b495a5f59033b8537a586a694f46f4c1e7c3a1e27f" }, "chart_with_downwards_trend": { "category": "objects", "moji": "📉", + "description": "chart with downwards trend", "unicodeVersion": "6.0", "digest": "5db7ccbc37665736a9c0b2f50247dcc09e404ec37f39db45b7b8b9464172a18c" }, "chart_with_upwards_trend": { "category": "objects", "moji": "📈", + "description": "chart with upwards trend", "unicodeVersion": "6.0", "digest": "bc4ea250b102fe5c09847e471478aff065ad3df755d9717896d38d887d9c6733" }, "checkered_flag": { "category": "travel", "moji": "🏁", + "description": "chequered flag", "unicodeVersion": "6.0", "digest": "0e77180e0cf9fc87e755a5a42cf23aec6bf30931db41331311e97ba0be178b78" }, "cheese": { "category": "food", "moji": "🧀", + "description": "cheese wedge", "unicodeVersion": "8.0", "digest": "50a6cb906c2120e2bbc0e22105924262007cfe1554d7b02b8cc84b6adedc6a0b" }, "cherries": { "category": "food", "moji": "🍒", + "description": "cherries", "unicodeVersion": "6.0", "digest": "13b8db9e7e6eec8509aa80c762966e1bf3538fcb1ac3d6eab18ee4da1528cf84" }, "cherry_blossom": { "category": "nature", "moji": "🌸", + "description": "cherry blossom", "unicodeVersion": "6.0", "digest": "af3083f5f8dd94936113f2e16caba5aec7a774d5589aa08bf5de82a2d278cc66" }, "chestnut": { "category": "nature", "moji": "🌰", + "description": "chestnut", "unicodeVersion": "6.0", "digest": "9f85b79b207a69ab81ab88dcef04954000965b039b4cf57de5f1b381745ab98b" }, "chicken": { "category": "nature", "moji": "🐔", + "description": "chicken", "unicodeVersion": "6.0", "digest": "57ceb4459d183740009caac6ebed089d2f1e12f67c138e1be1d0f992313c0ac4" }, "children_crossing": { "category": "symbols", "moji": "🚸", + "description": "children crossing", "unicodeVersion": "6.0", "digest": "0ded7d9aca0161e8ef8e2858c3c198e70e4badc7105ac3a6886e06975de19106" }, "chipmunk": { "category": "nature", "moji": "🐿", + "description": "chipmunk", "unicodeVersion": "7.0", "digest": "5b0dc1a859163097727ba2ba5ffca38b0a54d925eebb089977d28d0b4d917a3f" }, "chocolate_bar": { "category": "food", "moji": "🍫", + "description": "chocolate bar", "unicodeVersion": "6.0", "digest": "dd273e5050488acaf885f8a18b6e2b3901f69c5b39fa6465fb60621783d4109a" }, "christmas_tree": { "category": "nature", "moji": "🎄", + "description": "christmas tree", "unicodeVersion": "6.0", "digest": "ce60cbe2ebbe8057be8edea2392455fedd2bcda64a0a831f6a1942028af7e747" }, "church": { "category": "travel", "moji": "⛪", + "description": "church", "unicodeVersion": "5.2", "digest": "2c328456528f7336e59443e20ec3ab22fe71f1fccb1dd50d0ad68eb206937557" }, "cinema": { "category": "symbols", "moji": "🎦", + "description": "cinema", "unicodeVersion": "6.0", "digest": "4c26dcdc76f93dbc2a1dc49ed4e132b8e8f2b7cdc1acf5e09b3dfd99430d97cd" }, "circus_tent": { "category": "activity", "moji": "🎪", + "description": "circus tent", "unicodeVersion": "6.0", "digest": "fec5f2a06222be8be549178b29720343cc00145177ec387ca4e6f3432481fe77" }, "city_dusk": { "category": "travel", "moji": "🌆", + "description": "cityscape at dusk", "unicodeVersion": "6.0", "digest": "bba345e949dcc51f5f018220f000223797970c82ead2ab9c822f9dc0847aa155" }, "city_sunset": { "category": "travel", "moji": "🌇", + "description": "sunset over buildings", "unicodeVersion": "6.0", "digest": "a846df1a4c7c778f8e1729804aece86eb29d2fcb95dc39eaaf2aae1897f3dcc7" }, "cityscape": { "category": "travel", "moji": "🏙", + "description": "cityscape", "unicodeVersion": "7.0", "digest": "ee360be7514c4bfb0d539dd28f3b2031ebcef04e850723ec0685fb54bd8e6d5f" }, "cl": { "category": "symbols", "moji": "🆑", + "description": "squared cl", "unicodeVersion": "6.0", "digest": "fcec2855dbad9fda11d6e2802bc0dcaabab0b5be233508f5e439f156f07602c1" }, "clap": { "category": "people", "moji": "👏", + "description": "clapping hands sign", "unicodeVersion": "6.0", "digest": "a1860ce7812a9f6fb55e45761e1b79a2f8f0620eb04f80748a38420889d58a2a" }, "clap_tone1": { "category": "people", "moji": "👏🏻", + "description": "clapping hands sign tone 1", "unicodeVersion": "8.0", "digest": "18a7022e08223fb2109af5a9b9a5b4f47dc870ce4453f4987d2d0b729ef54586" }, "clap_tone2": { "category": "people", "moji": "👏🏼", + "description": "clapping hands sign tone 2", "unicodeVersion": "8.0", "digest": "5954c8658b15e755d2018d8674df84d38e22ffededc4d726c6a33b709f71426a" }, "clap_tone3": { "category": "people", "moji": "👏🏽", + "description": "clapping hands sign tone 3", "unicodeVersion": "8.0", "digest": "22639b6bd3c53784a2f855d6db7bdf31621519f19dfc29a6bc310eee6421f742" }, "clap_tone4": { "category": "people", "moji": "👏🏾", + "description": "clapping hands sign tone 4", "unicodeVersion": "8.0", "digest": "e55248dc163d1bbd118b50cd8767750ead86d082151febbc0a75b32d63abceec" }, "clap_tone5": { "category": "people", "moji": "👏🏿", + "description": "clapping hands sign tone 5", "unicodeVersion": "8.0", "digest": "76046b8157dabbe048a07fc318122456020c9c980fc1b8ab76802330e07b3b53" }, "clapper": { "category": "activity", "moji": "🎬", + "description": "clapper board", "unicodeVersion": "6.0", "digest": "8149752a0e3e8abede2d433d1afab6d217877d0c76adb1e2845a0142c0cdcbaa" }, "classical_building": { "category": "travel", "moji": "🏛", + "description": "classical building", "unicodeVersion": "7.0", "digest": "9ee0d00c43d6e22b6a3ddea67619737270cc7e9294797a19c7c60d5f92aa44fa" }, "clipboard": { "category": "objects", "moji": "📋", + "description": "clipboard", "unicodeVersion": "6.0", "digest": "bdd7f7d973c714e59d2903d401a876e6018794c7987c9ca57108c137c5edc25f" }, "clock": { "category": "objects", "moji": "🕰", + "description": "mantlepiece clock", "unicodeVersion": "7.0", "digest": "302835eab2637db799acf69b3d795571ef3432251267050db0704f2954e8b190" }, "clock1": { "category": "symbols", "moji": "🕐", + "description": "clock face one oclock", "unicodeVersion": "6.0", "digest": "1778eec07ce061c9393e5abee5ca83b24e1ce61d8a75fa2e39efcb31aa160395" }, "clock10": { "category": "symbols", "moji": "🕙", + "description": "clock face ten oclock", "unicodeVersion": "6.0", "digest": "601fc12ea5280a54c2e69dbb685f454e4165fe771756ed6f89016e29e683a24f" }, "clock1030": { "category": "symbols", "moji": "🕥", + "description": "clock face ten-thirty", "unicodeVersion": "6.0", "digest": "4fd155f08f797542d52cff4b0aa3ca9f080f37a41c301b82f90ff6d4693c890e" }, "clock11": { "category": "symbols", "moji": "🕚", + "description": "clock face eleven oclock", "unicodeVersion": "6.0", "digest": "5c79dc812e812e8a01993ea633b323d654ce3a7ea258692781a4896e4ad2017e" }, "clock1130": { "category": "symbols", "moji": "🕦", + "description": "clock face eleven-thirty", "unicodeVersion": "6.0", "digest": "41497ee2020ee5ac9aa5f9b07560f7afca7c422b04214449cfc5cea9f020f52e" }, "clock12": { "category": "symbols", "moji": "🕛", + "description": "clock face twelve oclock", "unicodeVersion": "6.0", "digest": "046bb7ffa5f5d27c2e3411ba543484d9dabb8ebf6d6e7a7e9bfb088c1813500c" }, "clock1230": { "category": "symbols", "moji": "🕧", + "description": "clock face twelve-thirty", "unicodeVersion": "6.0", "digest": "bbfe9db5a2043aaba19a7a2a0185c7efcebf1e8c9263b8233f75b53c4825f0f4" }, "clock130": { "category": "symbols", "moji": "🕜", + "description": "clock face one-thirty", "unicodeVersion": "6.0", "digest": "8662cb395ee680c2781123305c4c8ce8c0df9565c2c942668940be540cc0c094" }, "clock2": { "category": "symbols", "moji": "🕑", + "description": "clock face two oclock", "unicodeVersion": "6.0", "digest": "42f7429748b612dce7de77221cbbc710655811f7bb23e2a986c36e6d662f0ec4" }, "clock230": { "category": "symbols", "moji": "🕝", + "description": "clock face two-thirty", "unicodeVersion": "6.0", "digest": "e710b6ef14227cd240ea3e2a867c8ef45b5c060adf3cb30ba9077c2351fe6677" }, "clock3": { "category": "symbols", "moji": "🕒", + "description": "clock face three oclock", "unicodeVersion": "6.0", "digest": "7340d465b398a378211dff9ec806db579d061206fd6fc238623d070cfe0a55ce" }, "clock330": { "category": "symbols", "moji": "🕞", + "description": "clock face three-thirty", "unicodeVersion": "6.0", "digest": "7aa4a15cc8de04ed3bdeb0f8a54a7915065f2809a07054e002d89926c9766831" }, "clock4": { "category": "symbols", "moji": "🕓", + "description": "clock face four oclock", "unicodeVersion": "6.0", "digest": "36fd88e81ad488b0ec49a911a838693281573fa14736ae4a6dd1c40a4ff69bb1" }, "clock430": { "category": "symbols", "moji": "🕟", + "description": "clock face four-thirty", "unicodeVersion": "6.0", "digest": "7bd5dd71e89d95dcf18b9e8c1fe2a353a7da3b69aadb8dda80ee9bafb05da58d" }, "clock5": { "category": "symbols", "moji": "🕔", + "description": "clock face five oclock", "unicodeVersion": "6.0", "digest": "aa406409e56a0bfd8c850e44efe45fd190ffd7bf7061e934ed7928dfbdfc9eba" }, "clock530": { "category": "symbols", "moji": "🕠", + "description": "clock face five-thirty", "unicodeVersion": "6.0", "digest": "25dd3bcc53ddd98eeea498d7dbd4c306ef39dd033f15909063388a0800febf41" }, "clock6": { "category": "symbols", "moji": "🕕", + "description": "clock face six oclock", "unicodeVersion": "6.0", "digest": "0a321eaf1bc5db8436bbadac66c45ba257fc98ad4c7569ce3fc6602c824b6d7c" }, "clock630": { "category": "symbols", "moji": "🕡", + "description": "clock face six-thirty", "unicodeVersion": "6.0", "digest": "55a4c5a665fdd38a724e9357a93c55401fcd5f1b13078c25754bd70c3fc4ccec" }, "clock7": { "category": "symbols", "moji": "🕖", + "description": "clock face seven oclock", "unicodeVersion": "6.0", "digest": "6154306545716e865da0ec537ee4f22bfe6c7294502a64a2dcf425c587d0e2a2" }, "clock730": { "category": "symbols", "moji": "🕢", + "description": "clock face seven-thirty", "unicodeVersion": "6.0", "digest": "6925654de642e50f84661f94364a96c87757d73fffe766aacbf4bbd70130547b" }, "clock8": { "category": "symbols", "moji": "🕗", + "description": "clock face eight oclock", "unicodeVersion": "6.0", "digest": "9be2d189c7ea56d39fd259f84853d753c1cf33e64f8ed57f86f822d9ae23a1ee" }, "clock830": { "category": "symbols", "moji": "🕣", + "description": "clock face eight-thirty", "unicodeVersion": "6.0", "digest": "16878613c0000d2f558c88d080551f424a8bd9df1358e0f931dd25c3da68f2d9" }, "clock9": { "category": "symbols", "moji": "🕘", + "description": "clock face nine oclock", "unicodeVersion": "6.0", "digest": "1d1e7e3c9d085ffa5b7c0f3d9fd394b734f16ae3b60df09af50fe6c8d4f3c8bb" }, "clock930": { "category": "symbols", "moji": "🕤", + "description": "clock face nine-thirty", "unicodeVersion": "6.0", "digest": "9fdef6a4939315c017b165e1dbac7710fb335df8c309be3fe2a011ef7fc28d74" }, "closed_book": { "category": "objects", "moji": "📕", + "description": "closed book", "unicodeVersion": "6.0", "digest": "b18288629d201bfdfc5d66ec47df89809d00642b15732757e6a04789f36a7d9f" }, "closed_lock_with_key": { "category": "objects", "moji": "🔐", + "description": "closed lock with key", "unicodeVersion": "6.0", "digest": "e39adfe9b30973bca16472c2b7e6462b064a93b9d452aa48edd74c727641a83d" }, "closed_umbrella": { "category": "people", "moji": "🌂", + "description": "closed umbrella", "unicodeVersion": "6.0", "digest": "2cc0592c74601f7439e88c3c1ec4f05e3459608ef1ea6558c5824ed7c3889727" }, "cloud": { "category": "nature", "moji": "☁", + "description": "cloud", "unicodeVersion": "1.1", "digest": "5b3a19718dfa8a381929665afdc2284464d24020c8dd0caff4dad465a1f536ba" }, "cloud_lightning": { "category": "nature", "moji": "🌩", + "description": "cloud with lightning", "unicodeVersion": "7.0", "digest": "2b32f6d87726df2935ad81870879ccec30ce9b4fd5861d1a6317f9eca2f013d9" }, "cloud_rain": { "category": "nature", "moji": "🌧", + "description": "cloud with rain", "unicodeVersion": "7.0", "digest": "1e1e8bc59e168e1d2e72bf11f2d43cb578cbf0a5f1daf383bba5c56fb750ee71" }, "cloud_snow": { "category": "nature", "moji": "🌨", + "description": "cloud with snow", "unicodeVersion": "7.0", "digest": "2d364f859b83e684213e8eece1640208d80a8de0a49d0fc8e0e24c5a8493a3b1" }, "cloud_tornado": { "category": "nature", "moji": "🌪", + "description": "cloud with tornado", "unicodeVersion": "7.0", "digest": "7cbed2343c280ba3996082b3d0fb9d8cd57d6e62fe6c9ecb159f46b4a2e49151" }, "clown": { "category": "people", "moji": "🤡", + "description": "clown face", "unicodeVersion": "9.0", "digest": "eea95687caabc9e808514c2450ba599e5e24ef47923dbec86f5297a64438e2e5" }, "clubs": { "category": "symbols", "moji": "♣", + "description": "black club suit", "unicodeVersion": "1.1", "digest": "b8cf72ecd8568ced077b475d94788fb282bdb06d25031b5d54dd63e25effb138" }, "cocktail": { "category": "food", "moji": "🍸", + "description": "cocktail glass", "unicodeVersion": "6.0", "digest": "3792def2cde885cf32167f04904d3b0b788388e8af410c63e4cd31550feba775" }, "coffee": { "category": "food", "moji": "☕", + "description": "hot beverage", "unicodeVersion": "4.0", "digest": "0d29615a7a67d3aafa257b909bb915dc74fa8f854acb0d9a2c29e94eedf80326" }, "coffin": { "category": "objects", "moji": "⚰", + "description": "coffin", "unicodeVersion": "4.1", "digest": "78eccc1aad2a822649fba8503d4d30354bef367c4271193c40ddb692308f9db8" }, "cold_sweat": { "category": "people", "moji": "😰", + "description": "face with open mouth and cold sweat", "unicodeVersion": "6.0", "digest": "f53aab523ed3fa2224a16881d263fb5e039f163380f92feb2c63c20f9b14dcd2" }, "comet": { "category": "nature", "moji": "☄", + "description": "comet", "unicodeVersion": "1.1", "digest": "40ce93e55c6e57a88d80670b37171190bd5ffc87b7078891d8de5b15795385c5" }, "compression": { "category": "objects", "moji": "🗜", + "description": "compression", "unicodeVersion": "7.0", "digest": "c8841f7afb5345f1c31da116a7fb41d07232ea58d3f7f1a75c5890aa1a80bfd6" }, "computer": { "category": "objects", "moji": "💻", + "description": "personal computer", "unicodeVersion": "6.0", "digest": "c970ce76b5607434895b0407bdaa93140f887930781a17dd7dcf16f711451d93" }, "confetti_ball": { "category": "objects", "moji": "🎊", + "description": "confetti ball", "unicodeVersion": "6.0", "digest": "a638b16f1acdbcf69edf760161b1bd7ff1fd5426c5b1203ad9d294dcc0701f10" }, "confounded": { "category": "people", "moji": "😖", + "description": "confounded face", "unicodeVersion": "6.0", "digest": "e2ff3b4df65d00c1ca9ae0cb379f959ea2cecefb3d676d4f8c2c5f2c103da4f6" }, "confused": { "category": "people", "moji": "😕", + "description": "confused face", "unicodeVersion": "6.1", "digest": "118d7f830ec08a3ac4b798eebb77a989b8c142f2588727181be4a2548e3c4f06" }, "congratulations": { "category": "symbols", "moji": "㊗", + "description": "circled ideograph congratulation", "unicodeVersion": "1.1", "digest": "02fd1338c54fe5f9a0fd861f23c56edc1d39bcd3140b68f0f626f9e2494d2d1c" }, "construction": { "category": "travel", "moji": "🚧", + "description": "construction sign", "unicodeVersion": "6.0", "digest": "c3a0401331111b9eda1206bee5f322db80b0870547d307b10dcac1314e4078c8" }, "construction_site": { "category": "travel", "moji": "🏗", + "description": "building construction", "unicodeVersion": "7.0", "digest": "c611f0a5de10f000a0756935f226845c7292f19ff5581d1f7a7554316338bbcb" }, "construction_worker": { "category": "people", "moji": "👷", + "description": "construction worker", "unicodeVersion": "6.0", "digest": "8c094733987e7c4da8d3aa4588b530ae07042bd70cf337b1fd412a70ee8f0ed6" }, "construction_worker_tone1": { "category": "people", "moji": "👷🏻", + "description": "construction worker tone 1", "unicodeVersion": "8.0", "digest": "fcd927405fef4486105cd3aff62155467d21cebbc013924d4b52b717b566602b" }, "construction_worker_tone2": { "category": "people", "moji": "👷🏼", + "description": "construction worker tone 2", "unicodeVersion": "8.0", "digest": "d1ec773828936c703dd6e334e696dc3cf7c34c0a8ec691564a384b735cdeaaba" }, "construction_worker_tone3": { "category": "people", "moji": "👷🏽", + "description": "construction worker tone 3", "unicodeVersion": "8.0", "digest": "37c114d6879b9b32b800b0d4cf770dcbe04d1455698130ecd709a0cb9dea880b" }, "construction_worker_tone4": { "category": "people", "moji": "👷🏾", + "description": "construction worker tone 4", "unicodeVersion": "8.0", "digest": "5264996c1bedb6061a0dfdddce233d863bf308d27127ad152b63bfd983162cf7" }, "construction_worker_tone5": { "category": "people", "moji": "👷🏿", + "description": "construction worker tone 5", "unicodeVersion": "8.0", "digest": "87051aec81fd5dfd4dc44ff0411a528ee08253e9494d37efa550694e28dde6d3" }, "control_knobs": { "category": "objects", "moji": "🎛", + "description": "control knobs", "unicodeVersion": "7.0", "digest": "0d7f33ff7acc1cc3a81e6a786ff007df20da145e3070f338505dfed5100e9fcb" }, "convenience_store": { "category": "travel", "moji": "🏪", + "description": "convenience store", "unicodeVersion": "6.0", "digest": "975dcf9b8e9e3fb1e29574b41300b9d96fd64703b3c18ff52f9f1875d1cf1b52" }, "cookie": { "category": "food", "moji": "🍪", + "description": "cookie", "unicodeVersion": "6.0", "digest": "4bed3522bd50091ac5b68ca760661eb484d7f1b9c9d564d2097bd812b7f28ae4" }, "cooking": { "category": "food", "moji": "🍳", + "description": "cooking", "unicodeVersion": "6.0", "digest": "563ffd6cae381ce1e318cdacc54e70040d6a01a50d0db8aeb50edbbe413eac58" }, "cool": { "category": "symbols", "moji": "🆒", + "description": "squared cool", "unicodeVersion": "6.0", "digest": "5739a37341c782a4736adfce804e12776ae33081098a3d052d8ae9a64b4d22d1" }, "cop": { "category": "people", "moji": "👮", + "description": "police officer", "unicodeVersion": "6.0", "digest": "78996521bbe231d03ebea355226d8a1515f47cde7b2fbeca1037e7b7e5133466" }, "cop_tone1": { "category": "people", "moji": "👮🏻", + "description": "police officer tone 1", "unicodeVersion": "8.0", "digest": "8a38cd107f5f4c0b821ac43f32df5dc57facaf39fbafb98483ec00fd7df41baf" }, "cop_tone2": { "category": "people", "moji": "👮🏼", + "description": "police officer tone 2", "unicodeVersion": "8.0", "digest": "8ab8ab086f3ff82aa4bf4760c3c822846ec2696c41d21dffdac12d5afbe398b7" }, "cop_tone3": { "category": "people", "moji": "👮🏽", + "description": "police officer tone 3", "unicodeVersion": "8.0", "digest": "fce710a99fd44a7c8af3ea01b2007e46d3ff38d7a0dff1ef26d6f893ede7e6d2" }, "cop_tone4": { "category": "people", "moji": "👮🏾", + "description": "police officer tone 4", "unicodeVersion": "8.0", "digest": "3017dd73ef475379911c5e6c79bd0f9f533dbbc5057bce6a11244faa12996ba0" }, "cop_tone5": { "category": "people", "moji": "👮🏿", + "description": "police officer tone 5", "unicodeVersion": "8.0", "digest": "a3b8807b3f2a8d6ee9bcec0339355bda486e8c930f727139f5447a4b046a6307" }, "copyright": { "category": "symbols", "moji": "©", + "description": "copyright sign", "unicodeVersion": "1.1", "digest": "cc28663cdd3f8333d9bb57b511348cde4e51bda19cf0629dccb05c8fc425e079" }, "corn": { "category": "food", "moji": "🌽", + "description": "ear of maize", "unicodeVersion": "6.0", "digest": "a099a0b291fa758690e6ee6c762b9ade9a0e3350a707c52d968dfffbcc467de5" }, "couch": { "category": "objects", "moji": "🛋", + "description": "couch and lamp", "unicodeVersion": "7.0", "digest": "84cd734dbaa7f9f519438036d687e7a53217130779bc3de30258f163521b9474" }, "couple": { "category": "people", "moji": "👫", + "description": "man and woman holding hands", "unicodeVersion": "6.0", "digest": "c897ba76e24e2f43a4aa261c2754800a8473f43c7ce53f9909a6af2c4897732a" }, "couple_mm": { "category": "people", "moji": "👨‍❤️‍👨", + "description": "couple (man,man)", "unicodeVersion": "6.0", "digest": "c812471d35d46e12270653039a907d1dfa2dea0defd65596283e5b8e03cea803" }, "couple_with_heart": { "category": "people", "moji": "💑", + "description": "couple with heart", "unicodeVersion": "6.0", "digest": "420bfa81bad10365550c77a98e1c07eb00d03663fe7b610fab1aca8a0a9d201b" }, "couple_ww": { "category": "people", "moji": "👩‍❤️‍👩", + "description": "couple (woman,woman)", "unicodeVersion": "6.0", "digest": "7ac49153a612d63302299eee996308b7dcafa0a152473dab679215036fe6567e" }, "couplekiss": { "category": "people", "moji": "💏", + "description": "kiss", "unicodeVersion": "6.0", "digest": "1acfef9d375c4c1deb235babd856b0f90ad4f3194751694cb6abb44f00f29e42" }, "cow": { "category": "nature", "moji": "🐮", + "description": "cow face", "unicodeVersion": "6.0", "digest": "d71c854ff8b343ee24b8c2b9d56c7cb3fc6fa1a6dc0d7a137841b9f646e6d71b" }, "cow2": { "category": "nature", "moji": "🐄", + "description": "cow", "unicodeVersion": "6.0", "digest": "e7a5131d7dee0f3356814b0ac1ea8ff280b12a7b580181e20ddb0b7eeb7e7339" }, "cowboy": { "category": "people", "moji": "🤠", + "description": "face with cowboy hat", "unicodeVersion": "9.0", "digest": "1aabf23f6b95a9b772fdb8eb45b8ec93584a5357f9131c6eabc9d1b83fe67e89" }, "crab": { "category": "nature", "moji": "🦀", + "description": "crab", "unicodeVersion": "8.0", "digest": "e6be16699fdb5d87f42f28f6cc141a44b7ffd834ecdd536813c4b5b86d3fc4a5" }, "crayon": { "category": "objects", "moji": "🖍", + "description": "lower left crayon", "unicodeVersion": "7.0", "digest": "b180d6afa4777861222a4228164ce284230fe90c589f52ffa9351bac777e901a" }, "credit_card": { "category": "objects", "moji": "💳", + "description": "credit card", "unicodeVersion": "6.0", "digest": "808cd120fd3738eb2be1f6c6c029d98387b0e03fca7d1451e8fbf9c5ab3f643f" }, "crescent_moon": { "category": "nature", "moji": "🌙", + "description": "crescent moon", "unicodeVersion": "6.0", "digest": "042e7e01e6e88b97a763b7cc41e2a2b3fe68a649bacf4a090cd28fc653baf640" }, "cricket": { "category": "activity", "moji": "🏏", + "description": "cricket bat and ball", "unicodeVersion": "8.0", "digest": "4c4559d0b4efe24cc248fa57f413541307992e519d0cb9fb8828637ac2f4cc16" }, "crocodile": { "category": "nature", "moji": "🐊", + "description": "crocodile", "unicodeVersion": "6.0", "digest": "59cb4164c50b6bc9ae311ce6f7610467c1aaafa848b5fff7614f064715f91992" }, "croissant": { "category": "food", "moji": "🥐", + "description": "croissant", "unicodeVersion": "9.0", "digest": "b751e287157a1e276617a841a5b5f7f1208ca226cfd8fa947f144390b65a5e16" }, "cross": { "category": "symbols", "moji": "✝", + "description": "latin cross", "unicodeVersion": "1.1", "digest": "a6b07c838fb75ef2ebefa2df6005e8d784753239ec03c37695a13e3b1954d653" }, "crossed_flags": { "category": "objects", "moji": "🎌", + "description": "crossed flags", "unicodeVersion": "6.0", "digest": "2841c671075e6f1a79c61c2d716423159fb0bc0786e3fb0049697766533bf262" }, "crossed_swords": { "category": "objects", "moji": "⚔", + "description": "crossed swords", "unicodeVersion": "4.1", "digest": "3771a5b26b514236521ce44e15f7730fa9148c6a782b9b600ab870a1f7de6f9f" }, "crown": { "category": "people", "moji": "👑", + "description": "crown", "unicodeVersion": "6.0", "digest": "6741e58d8f823194e0a3484ac1563e20d9e0b44c1bc46d82444dfffa092cdfc7" }, "cruise_ship": { "category": "travel", "moji": "🛳", + "description": "passenger ship", "unicodeVersion": "7.0", "digest": "2b7b62db5d118a632673564099e3405ea6d61ea9b8e123b5a2aaf011bb2a54a4" }, "cry": { "category": "people", "moji": "😢", + "description": "crying face", "unicodeVersion": "6.0", "digest": "fc3307ec4fe75539770c1123a0e8e721d9e021009a502655132f68d7cc453816" }, "crying_cat_face": { "category": "people", "moji": "😿", + "description": "crying cat face", "unicodeVersion": "6.0", "digest": "4942c24935c22babdcb8af41d2c0a7588356b6b674bc238902e2f10ad03e2c5b" }, "crystal_ball": { "category": "objects", "moji": "🔮", + "description": "crystal ball", "unicodeVersion": "6.0", "digest": "05f73b30b1e5b0fc66fb5dc6caddd2d547ee7b9d2f97513dc908ba1a2e352e30" }, "cucumber": { "category": "food", "moji": "🥒", + "description": "cucumber", "unicodeVersion": "9.0", "digest": "d1196e23f2f155ef5c1330f8497f40957a7357cb177127f457c5c471f0a23727" }, "cupid": { "category": "symbols", "moji": "💘", + "description": "heart with arrow", "unicodeVersion": "6.0", "digest": "246e71f44c6ebc2e4f887e25438e4f894e8cc92e06069e711b893ff391abb658" }, "curly_loop": { "category": "symbols", "moji": "➰", + "description": "curly loop", "unicodeVersion": "6.0", "digest": "9e4eb98d6597888f91208080c6a79824adb432ea34f46c85da26cb630bd1cc73" }, "currency_exchange": { "category": "symbols", "moji": "💱", + "description": "currency exchange", "unicodeVersion": "6.0", "digest": "b85377265b9876888969aa42b65bba0be523a370175baf226f20131e535af554" }, "curry": { "category": "food", "moji": "🍛", + "description": "curry and rice", "unicodeVersion": "6.0", "digest": "a01c0a713662817720b485f7739f57e61afc025f5c43792f4de961c94f92f31e" }, "custard": { "category": "food", "moji": "🍮", + "description": "custard", "unicodeVersion": "6.0", "digest": "85c2b9ac904134a6c3587eb0a0806f2ab4282c5ed5c79d41734f3203998f757e" }, "customs": { "category": "symbols", "moji": "🛃", + "description": "customs", "unicodeVersion": "6.0", "digest": "eb2546e1e617d4c1a1f614318af5e5dacf3e8d9479ffa08108977defa83ded32" }, "cyclone": { "category": "symbols", "moji": "🌀", + "description": "cyclone", "unicodeVersion": "6.0", "digest": "7a0f8564d76adf2d0ed272f56dc0d01fb7b557852e0ca797e73f5472b8630bf3" }, "dagger": { "category": "objects", "moji": "🗡", + "description": "dagger knife", "unicodeVersion": "7.0", "digest": "35a179168198d03295e626cc27d3b92d30a732c55a2ca75d7a11a0fbed414772" }, "dancer": { "category": "people", "moji": "💃", + "description": "dancer", "unicodeVersion": "6.0", "digest": "66ffa86827e85acae4aa870c0859fe3a9dad03d21ff4bc800b61c95c902a8a90" }, "dancer_tone1": { "category": "people", "moji": "💃🏻", + "description": "dancer tone 1", "unicodeVersion": "8.0", "digest": "bdbee740addc890e369d3469a3585eb0d1e4fbc7e04dd6f6aca762d8aeee6a8c" }, "dancer_tone2": { "category": "people", "moji": "💃🏼", + "description": "dancer tone 2", "unicodeVersion": "8.0", "digest": "9f7b4c627241eaa2def9717a5286a423f0b9c1b044dd9ea4442a76f1858d14a4" }, "dancer_tone3": { "category": "people", "moji": "💃🏽", + "description": "dancer tone 3", "unicodeVersion": "8.0", "digest": "a6bd49a377ce6c2004bf126b6f66d0b21d8c14103c2add7b10f12ed9e1c2d302" }, "dancer_tone4": { "category": "people", "moji": "💃🏾", + "description": "dancer tone 4", "unicodeVersion": "8.0", "digest": "4ec2a7629c01b0e9006b5cda4deae3bf297ce3b71d18063f93eeb5c14be19a1a" }, "dancer_tone5": { "category": "people", "moji": "💃🏿", + "description": "dancer tone 5", "unicodeVersion": "8.0", "digest": "2b48e3a6b366c6f55f73b816e6fb03c39e9890f586f7e9c9043cf0c013d9cdd5" }, "dancers": { "category": "people", "moji": "👯", + "description": "woman with bunny ears", "unicodeVersion": "6.0", "digest": "12be66ed19d232bb387270f40bece68bd0cb2342b318f6c9bb8b49c64ff7d0ad" }, "dango": { "category": "food", "moji": "🍡", + "description": "dango", "unicodeVersion": "6.0", "digest": "34e8cd153c50f2d725abe8934c35c96a3ab533f0cc5fbb1e1474eafad1dc1fc2" }, "dark_sunglasses": { "category": "people", "moji": "🕶", + "description": "dark sunglasses", "unicodeVersion": "7.0", "digest": "d0a735ad5bf0ece00af2a21abf950b89292ebd8ca6e28b1dbb1368252fb44afe" }, "dart": { "category": "activity", "moji": "🎯", + "description": "direct hit", "unicodeVersion": "6.0", "digest": "998642f06a875905e0a6bf30963c025baff1cf55b8e76884b9119f2d71188b0c" }, "dash": { "category": "nature", "moji": "💨", + "description": "dash symbol", "unicodeVersion": "6.0", "digest": "f7aae7d3887c67d76f3329c2dc9e6807dc580a4b07ab35599c7805e41823a345" }, "date": { "category": "objects", "moji": "📅", + "description": "calendar", "unicodeVersion": "6.0", "digest": "d0b695e4a7cfbbe71b4fbebf345b66ca98f0cf1c751362928e54c23ca78d4c7b" }, "deciduous_tree": { "category": "nature", "moji": "🌳", + "description": "deciduous tree", "unicodeVersion": "6.0", "digest": "3c70f1a77f2754f41c830e88d43b7d53c14311d64626ded164aa9ac7d2695790" }, "deer": { "category": "nature", "moji": "🦌", + "description": "deer", "unicodeVersion": "9.0", "digest": "7f4302ca68fd121ee73be48d0a0a0fb9e7e2741071a491ad2b7b0eab9f11ad25" }, "department_store": { "category": "travel", "moji": "🏬", + "description": "department store", "unicodeVersion": "6.0", "digest": "4be910d2efe74d8ce2c1f41d7753c8873579faca83fcf779a4887d8ab9e5923b" }, "desert": { "category": "travel", "moji": "🏜", + "description": "desert", "unicodeVersion": "7.0", "digest": "d4b1a11c5130debe042df6cc2b3389f15c68a5cb32dc1b3a82b78f733d0c9e4e" }, "desktop": { "category": "objects", "moji": "🖥", + "description": "desktop computer", "unicodeVersion": "7.0", "digest": "cde5bfb6c71bb7d663808a3561b24cb5b5560f95f510b40f81250cac1b21933e" }, "diamond_shape_with_a_dot_inside": { "category": "symbols", "moji": "💠", + "description": "diamond shape with a dot inside", "unicodeVersion": "6.0", "digest": "e91323577ab89e95b0fa0b9272ea0c797b76908f24d36992630e9325273a4ce3" }, "diamonds": { "category": "symbols", "moji": "♦", + "description": "black diamond suit", "unicodeVersion": "1.1", "digest": "bf3d9a020afe8aa226db73590bc193a9c2c3e6e642edd2445c5960c3e67cf153" }, "disappointed": { "category": "people", "moji": "😞", + "description": "disappointed face", "unicodeVersion": "6.0", "digest": "c0f406c6beea0fd1328adefc097d04aa16b72f7a5afa0867967d8ea25d72db17" }, "disappointed_relieved": { "category": "people", "moji": "😥", + "description": "disappointed but relieved face", "unicodeVersion": "6.0", "digest": "c826f5dd4f2f7e5289d720851d4826ab8284d915606c1b152ab229b7fadbba14" }, "dividers": { "category": "objects", "moji": "🗂", + "description": "card index dividers", "unicodeVersion": "7.0", "digest": "4b2c653b18cf0fa31f1f0ac94a6fbd214ea0d1b0a90a450ab6e169906fc5764f" }, "dizzy": { "category": "nature", "moji": "💫", + "description": "dizzy symbol", "unicodeVersion": "6.0", "digest": "d577545c2de42389695447c6ebbfef895f30f0fda84eef45684f9bf4a9c27ff1" }, "dizzy_face": { "category": "people", "moji": "😵", + "description": "dizzy face", "unicodeVersion": "6.0", "digest": "7b3aeaffb4e15ccf633b91dda4a44847a1eb28d78ce58b4d171b20a771bde414" }, "do_not_litter": { "category": "symbols", "moji": "🚯", + "description": "do not litter symbol", "unicodeVersion": "6.0", "digest": "98b07fbbcdb438d1b8a755869fa2de8e180a77fce359ec830eb46d38ec3e67cb" }, "dog": { "category": "nature", "moji": "🐶", + "description": "dog face", "unicodeVersion": "6.0", "digest": "3b31ce067b13e463284ce85536512cb1f8cd8b52fe73659f69971d0d6c1dfc11" }, "dog2": { "category": "nature", "moji": "🐕", + "description": "dog", "unicodeVersion": "6.0", "digest": "0a8901bce5ed994533ff84299b2a1364de28d872c9f9510d3426a83e8a9d2e34" }, "dollar": { "category": "objects", "moji": "💵", + "description": "banknote with dollar sign", "unicodeVersion": "6.0", "digest": "52438e38867aedc021740bb41f9ba336e75a50faa148419412a01d75d8c93155" }, "dolls": { "category": "objects", "moji": "🎎", + "description": "japanese dolls", "unicodeVersion": "6.0", "digest": "a687184e9a0915deef44bb3cacfb19d3f3f19cf2c110f1da90191dd567333c57" }, "dolphin": { "category": "nature", "moji": "🐬", + "description": "dolphin", "unicodeVersion": "6.0", "digest": "0b7ee08f4236232ca533ed3a3023d28020d36f178efaec5ce8b0e13a84778512" }, "door": { "category": "objects", "moji": "🚪", + "description": "door", "unicodeVersion": "6.0", "digest": "984a9ca88852ebdb539e0c385d9c6ffe5010e9189bc372a3d00f5c8d44c8e6f5" }, "doughnut": { "category": "food", "moji": "🍩", + "description": "doughnut", "unicodeVersion": "6.0", "digest": "27634587e6a53807baa32157bb06b0e115c8ad8aefebba7ebb0b65a084170e3a" }, "dove": { "category": "nature", "moji": "🕊", + "description": "dove of peace", "unicodeVersion": "7.0", "digest": "7c665f8594ffa53e72b01647e9d27360fb87d52d02fe9f20fc5fda08f9797dc3" }, "dragon": { "category": "nature", "moji": "🐉", + "description": "dragon", "unicodeVersion": "6.0", "digest": "2abcb3d945d848e34ffc76203b29ef26df7458856166fffd155611f7bbe72652" }, "dragon_face": { "category": "nature", "moji": "🐲", + "description": "dragon face", "unicodeVersion": "6.0", "digest": "0030548931b931e3b51f26cf660394aee36499e688ba83ce9cfccb635dcd4d54" }, "dress": { "category": "people", "moji": "👗", + "description": "dress", "unicodeVersion": "6.0", "digest": "96ceba928fb356f7c0ae99bf22552321f08a65d5f1c0340ab89641219ad366ad" }, "dromedary_camel": { "category": "nature", "moji": "🐪", + "description": "dromedary camel", "unicodeVersion": "6.0", "digest": "e06ef69c29f0fb12481727c0b4124e700572d3d7955e173279320f43f286518d" }, "drooling_face": { "category": "people", "moji": "🤤", + "description": "drooling face", "unicodeVersion": "9.0", "digest": "5203cb05cd266d7a7c929ab40364ad68571d380d9c7ff93a8d6d55261abaa1ba" }, "droplet": { "category": "nature", "moji": "💧", + "description": "droplet", "unicodeVersion": "6.0", "digest": "6475b4a4460a672c436a68f282ac97fb31e2934db4b80620063ee816159aa7c3" }, "drum": { "category": "activity", "moji": "🥁", + "description": "drum with drumsticks", "unicodeVersion": "9.0", "digest": "0d0639980b1a5dcbf1c3e7ef47263fb6543b871242c58452a8c2f642525d9dd8" }, "duck": { "category": "nature", "moji": "🦆", + "description": "duck", "unicodeVersion": "9.0", "digest": "8f8373798a7727368b32328e7a9a349727a949e7391ddd243b6456141a4f7e94" }, "dvd": { "category": "objects", "moji": "📀", + "description": "dvd", "unicodeVersion": "6.0", "digest": "3b7903285d91277181c26fdc9df857761bbac509d352e320c2519ea3b132704f" }, "e-mail": { "category": "objects", "moji": "📧", + "description": "e-mail symbol", "unicodeVersion": "6.0", "digest": "39b5a57a2376e4a1137e381be02a1775bd580e0371438f5297a401ea634f1830" }, "eagle": { "category": "nature", "moji": "🦅", + "description": "eagle", "unicodeVersion": "9.0", "digest": "b44fd4f61b83c5114358a272343ac9b0eabbc70847f739bbdbf8aae3ade5bc1d" }, "ear": { "category": "people", "moji": "👂", + "description": "ear", "unicodeVersion": "6.0", "digest": "4fdeb5a46e69311ecfd09c5b45c9018c24b625e28475cca8fa516b086ef952f8" }, "ear_of_rice": { "category": "nature", "moji": "🌾", + "description": "ear of rice", "unicodeVersion": "6.0", "digest": "2997c340c2b333d6ba9b73f94ff1a1881735fe0cc4f0c72d7719b305499fc425" }, "ear_tone1": { "category": "people", "moji": "👂🏻", + "description": "ear tone 1", "unicodeVersion": "8.0", "digest": "5ca759b8569a377a4e63e30d94b585b9f76d15348a8a0c1ba19fdc522790615e" }, "ear_tone2": { "category": "people", "moji": "👂🏼", + "description": "ear tone 2", "unicodeVersion": "8.0", "digest": "12aafb3ef2cfcdc892b2877c2e24920620f0f77f850e12afbfe55eadce9e37df" }, "ear_tone3": { "category": "people", "moji": "👂🏽", + "description": "ear tone 3", "unicodeVersion": "8.0", "digest": "f4d28d9f72cf116ac92d80061eb84c918d6523bf53b2ad526f5457aba487d527" }, "ear_tone4": { "category": "people", "moji": "👂🏾", + "description": "ear tone 4", "unicodeVersion": "8.0", "digest": "eaa9453670f7e3adc6ec6934ee70efc9bf60fe6c99c5804b7ba9e3804aec65de" }, "ear_tone5": { "category": "people", "moji": "👂🏿", + "description": "ear tone 5", "unicodeVersion": "8.0", "digest": "54bd0782419489556b80e9e0d15b05df74757aa4e04ba565f45c20d3dd60e3f1" }, "earth_africa": { "category": "nature", "moji": "🌍", + "description": "earth globe europe-africa", "unicodeVersion": "6.0", "digest": "c691a6f591f5a07b268fd64efe113e81cec8d5963ad83ced2537422343ff7ecf" }, "earth_americas": { "category": "nature", "moji": "🌎", + "description": "earth globe americas", "unicodeVersion": "6.0", "digest": "a9c60cf8341ff59a9cc1a715b7144af734fcd28915a8e003a31ebf2abf9aedb1" }, "earth_asia": { "category": "nature", "moji": "🌏", + "description": "earth globe asia-australia", "unicodeVersion": "6.0", "digest": "ee2beb61fb8c87279161c5a8c4ad17bb71ce790123f8fa33522941d027e060a5" }, "egg": { "category": "food", "moji": "🥚", + "description": "egg", "unicodeVersion": "9.0", "digest": "72b9c841af784e7cbccbbe48ba833df5cecdd284397c199cab079872e879d92f" }, "eggplant": { "category": "food", "moji": "🍆", + "description": "aubergine", "unicodeVersion": "6.0", "digest": "ec0a460e0cf0e615f51279677594a899672e1b4ecd9396e17a8cfa2a3efe5238" }, "eight": { "category": "symbols", "moji": "8️⃣", + "description": "keycap digit eight", "unicodeVersion": "3.0", "digest": "57ff905033a32747690adba6486d12b09eb4d45de556f4e1ab6fb04e1fb861a8" }, "eight_pointed_black_star": { "category": "symbols", "moji": "✴", + "description": "eight pointed black star", "unicodeVersion": "1.1", "digest": "7bf11f6e28591e3d0625296aaabf4ecb75c982e425abf3049339e93494acc17e" }, "eight_spoked_asterisk": { "category": "symbols", "moji": "✳", + "description": "eight spoked asterisk", "unicodeVersion": "1.1", "digest": "bb0758e7cc0e357285937671a91489bd32ce9d248eecdcc9c275a53a66325b26" }, "eject": { "category": "symbols", "moji": "⏏", + "description": "eject symbol", "unicodeVersion": "4.0", "digest": "eeb0cd23ead0c965e307de517a6805265f0c780c3e454e64bc4c1425dfe7548e" }, "electric_plug": { "category": "objects", "moji": "🔌", + "description": "electric plug", "unicodeVersion": "6.0", "digest": "b10ce87af86fa4f4022572ceb5ecd73bea867347a86832a7ea248364b0aad8d0" }, "elephant": { "category": "nature", "moji": "🐘", + "description": "elephant", "unicodeVersion": "6.0", "digest": "b7750f4b013fbd28ac5330e1694ef4d3b4a9c6fc7b807879db0c24b035a16c29" }, "end": { "category": "symbols", "moji": "🔚", + "description": "end with leftwards arrow above", "unicodeVersion": "6.0", "digest": "dd93aee6986eb637a8b58f234da47568b88525599f73246e322af030351997a2" }, "envelope": { "category": "objects", "moji": "✉", + "description": "envelope", "unicodeVersion": "1.1", "digest": "f5a512022a2f5280f372ff39c22cbda815f698710ca66f8f8c4d08418f98ca78" }, "envelope_with_arrow": { "category": "objects", "moji": "📩", + "description": "envelope with downwards arrow above", "unicodeVersion": "6.0", "digest": "f8643212e6a94f58ccf2bcedc54c5fda8ebeab274f4a8803f253de5f50ddb1d6" }, "euro": { "category": "objects", "moji": "💶", + "description": "banknote with euro sign", "unicodeVersion": "6.0", "digest": "3af3e223e8f26468a94f6f5c17198432656e8d20b3bab31566c2b5a86e717df4" }, "european_castle": { "category": "travel", "moji": "🏰", + "description": "european castle", "unicodeVersion": "6.0", "digest": "21082d0be7e3b2794e59ff0170da0cfe42a9b734cf02704603e3b52ff48202ba" }, "european_post_office": { "category": "travel", "moji": "🏤", + "description": "european post office", "unicodeVersion": "6.0", "digest": "02b4c7602939f0cb9cb2b4e05996bcdb6bd93cf8025c2ea02db8cbe13ca397d0" }, "evergreen_tree": { "category": "nature", "moji": "🌲", + "description": "evergreen tree", "unicodeVersion": "6.0", "digest": "74b226098e66c0a94a92e0f22b9d631736e12dca72c34182c9d0ba56aa593172" }, "exclamation": { "category": "symbols", "moji": "❗", + "description": "heavy exclamation mark symbol", "unicodeVersion": "5.2", "digest": "45b87ae4593656d7da49ff5645fb6a2a18d582553295358da9f09f1ae8272445" }, "expressionless": { "category": "people", "moji": "😑", + "description": "expressionless face", "unicodeVersion": "6.1", "digest": "34e2a1c8121f4f0bc4ce33d226d8cc1a4ebf5260746df2b23e29eef24ee9372e" }, "eye": { "category": "people", "moji": "👁", + "description": "eye", "unicodeVersion": "7.0", "digest": "79ecff79c2edee630e72725b54e67ee2e96d24ca03fef2954a56a09c0a2227f8" }, "eye_in_speech_bubble": { "category": "symbols", "moji": "👁‍🗨", + "description": "eye in speech bubble", "unicodeVersion": "7.0", "digest": "c0050c026c2a3060723cab2df2603c1c7da7ed81faedb9ebe16cd89721928a55" }, "eyeglasses": { "category": "people", "moji": "👓", + "description": "eyeglasses", "unicodeVersion": "6.0", "digest": "d4a9585d6c43ef514a97c45c64607162e775a45544821f1470c6f8f25b93ab81" }, "eyes": { "category": "people", "moji": "👀", + "description": "eyes", "unicodeVersion": "6.0", "digest": "1d5cae0b9b2e51e1de54295685d7f0c72ee794e2e6335a95b1d056c7e77260e8" }, "face_palm": { "category": "people", "moji": "🤦", + "description": "face palm", "unicodeVersion": "9.0", "digest": "4ec873048b34b1bb34430724cf28e4bee6c0a9eee88ce39b9d1565047dc92420" }, "face_palm_tone1": { "category": "people", "moji": "🤦🏻", + "description": "face palm tone 1", "unicodeVersion": "9.0", "digest": "e93ef92b4c01dbea6c400e708e23dd36da92ccfbf5eb4f177b3b20c3a46bdc19" }, "face_palm_tone2": { "category": "people", "moji": "🤦🏼", + "description": "face palm tone 2", "unicodeVersion": "9.0", "digest": "22c8bf9fd9fa2ed9dca7a6397ed00ba6cfe9aeef2b0fb7b516ee4dda0df050ea" }, "face_palm_tone3": { "category": "people", "moji": "🤦🏽", + "description": "face palm tone 3", "unicodeVersion": "9.0", "digest": "c0b8bb9d2423e6787b6bdf1ca5a13f52853e4f48a9a1af0f2d4af1364fff022e" }, "face_palm_tone4": { "category": "people", "moji": "🤦🏾", + "description": "face palm tone 4", "unicodeVersion": "9.0", "digest": "f522ab186adcbb4549ea2c03500cdd7a86add548e43ebf7a54d58cc24deea072" }, "face_palm_tone5": { "category": "people", "moji": "🤦🏿", + "description": "face palm tone 5", "unicodeVersion": "9.0", "digest": "363507ae7178b5ec583635f47bcab10c897346f48b85d8759b1004c32cd8ad65" }, "factory": { "category": "travel", "moji": "🏭", + "description": "factory", "unicodeVersion": "6.0", "digest": "c7aeb61ed8b0ac5c91d5197c73f1e2bb801921c22a76bb82c7659d990680dcb0" }, "fallen_leaf": { "category": "nature", "moji": "🍂", + "description": "fallen leaf", "unicodeVersion": "6.0", "digest": "81fce04231d48db0e55f3697f930e9a7e3306bed5e35f1234e98c40a24ac5626" }, "family": { "category": "people", "moji": "👪", + "description": "family", "unicodeVersion": "6.0", "digest": "06f2ce63768ffe43b3d9b2a9660b34d043f37b3c91610dd62343ba21df8ecbe5" }, "family_mmb": { "category": "people", "moji": "👨‍👨‍👦", + "description": "family (man,man,boy)", "unicodeVersion": "6.0", "digest": "41a18405be796699a7eb7c36ab6f7d898e322749997f45387377acf5bb16a50f" }, "family_mmbb": { "category": "people", "moji": "👨‍👨‍👦‍👦", + "description": "family (man,man,boy,boy)", "unicodeVersion": "6.0", "digest": "87255d1d18c6971c8c083c818e598424c1bd717eed892478b7e9516639dbfb45" }, "family_mmg": { "category": "people", "moji": "👨‍👨‍👧", + "description": "family (man,man,girl)", "unicodeVersion": "6.0", "digest": "a132b1b8f10b318d8e23aee15dab4caa14528aeb3c89966d4bcc25fb54af72ad" }, "family_mmgb": { "category": "people", "moji": "👨‍👨‍👧‍👦", + "description": "family (man,man,girl,boy)", "unicodeVersion": "6.0", "digest": "eb2bc1966df406aaf38ce5a58db9324162799cdacf31f74f40e6384807a8efc2" }, "family_mmgg": { "category": "people", "moji": "👨‍👨‍👧‍👧", + "description": "family (man,man,girl,girl)", "unicodeVersion": "6.0", "digest": "24f3d60f98fbd6b687f7cacfb629390b90509a754036e5439ae5294759c0606b" }, "family_mwbb": { "category": "people", "moji": "👨‍👩‍👦‍👦", + "description": "family (man,woman,boy,boy)", "unicodeVersion": "6.0", "digest": "2f77692bcb9275c4df501b64a18401dcaf8c68b21f26fbdad59b1feab0c98fd1" }, "family_mwg": { "category": "people", "moji": "👨‍👩‍👧", + "description": "family (man,woman,girl)", "unicodeVersion": "6.0", "digest": "1a976d13127665d9386cebfdb24e5572dc499bda484c0ee05585886edc616130" }, "family_mwgb": { "category": "people", "moji": "👨‍👩‍👧‍👦", + "description": "family (man,woman,girl,boy)", "unicodeVersion": "6.0", "digest": "960ec2cbac13ef208e73644cd36711b83e6c070c36950f834f3669812839b7f8" }, "family_mwgg": { "category": "people", "moji": "👨‍👩‍👧‍👧", + "description": "family (man,woman,girl,girl)", "unicodeVersion": "6.0", "digest": "8353b03dfa5c24aba75a0abdfdac01603f593819d54b4c7f2f88aafb31da0c6a" }, "family_wwb": { "category": "people", "moji": "👩‍👩‍👦", + "description": "family (woman,woman,boy)", "unicodeVersion": "6.0", "digest": "07a5dd397718c553573689f6512f386729c13a12d5dc78be47c06405769cd98a" }, "family_wwbb": { "category": "people", "moji": "👩‍👩‍👦‍👦", + "description": "family (woman,woman,boy,boy)", "unicodeVersion": "6.0", "digest": "b627f460f1da0d47b0b662402940b2b77c9538d380d05436dfca4b456c50c939" }, "family_wwg": { "category": "people", "moji": "👩‍👩‍👧", + "description": "family (woman,woman,girl)", "unicodeVersion": "6.0", "digest": "2d6f373bed53f1028f0fbe9caf036465a351f37b9e00fca7d722cc5a1984f251" }, "family_wwgb": { "category": "people", "moji": "👩‍👩‍👧‍👦", + "description": "family (woman,woman,girl,boy)", "unicodeVersion": "6.0", "digest": "72be5c85e1621f73d6794edd6e428febdb366b9e4c816f7829897fd1ab34642b" }, "family_wwgg": { "category": "people", "moji": "👩‍👩‍👧‍👧", + "description": "family (woman,woman,girl,girl)", "unicodeVersion": "6.0", "digest": "c39e0916069460d2d9741bddf58e76f5d6a09254cba0eeb262345adf8630bc32" }, "fast_forward": { "category": "symbols", "moji": "⏩", + "description": "black right-pointing double triangle", "unicodeVersion": "6.0", "digest": "e7d2d8085cfd406c2b096e8dd147dd3722290a5727b1f7df185989526a2335ec" }, "fax": { "category": "objects", "moji": "📠", + "description": "fax machine", "unicodeVersion": "6.0", "digest": "ff85ffa440c5379c9b138ebe2d7912d6098da3b37a051b80442d5557b7f993b0" }, "fearful": { "category": "people", "moji": "😨", + "description": "fearful face", "unicodeVersion": "6.0", "digest": "b72bdf7d075d5c4e38bbd8512fb45fda2e85c9c8732a47e67575ae9f2ed4c5df" }, "feet": { "category": "nature", "moji": "🐾", + "description": "paw prints", "unicodeVersion": "6.0", "digest": "45aca538d3a9831a0c7de491e5656c17705c07b8f4ac8e85254656b608976016" }, "fencer": { "category": "activity", "moji": "🤺", + "description": "fencer", "unicodeVersion": "9.0", "digest": "5db00fa456af9f6c7cb88d300579dd63e426bcb97ad25486b664aff25c688e21" }, "ferris_wheel": { "category": "travel", "moji": "🎡", + "description": "ferris wheel", "unicodeVersion": "6.0", "digest": "24b4551b7b79a2a5fd73de61542f2b444f896a52030c5f29791c8fcfcc28b95c" }, "ferry": { "category": "travel", "moji": "⛴", + "description": "ferry", "unicodeVersion": "5.2", "digest": "5002a72af2e3c4cef9a36ad5987aeed7d99f96bfd13e56f78957315ec7e749a3" }, "field_hockey": { "category": "activity", "moji": "🏑", + "description": "field hockey stick and ball", "unicodeVersion": "8.0", "digest": "4ee091d96161ba719ab8fd6f2b03f96d902a6f22cffe0563b930618bb8ac2b67" }, "file_cabinet": { "category": "objects", "moji": "🗄", + "description": "file cabinet", "unicodeVersion": "7.0", "digest": "92914147bf93e6d64271ff99d217a18a9850a367d08a5f9f458ecf9311a5bbe9" }, "file_folder": { "category": "objects", "moji": "📁", + "description": "file folder", "unicodeVersion": "6.0", "digest": "62a42a929267cfbfdb795ead381c9657c343458bc5fca95ea8a0ab892c61d4f6" }, "film_frames": { "category": "objects", "moji": "🎞", + "description": "film frames", "unicodeVersion": "7.0", "digest": "4da212148cadb9c4ea91e60d2d8316e38cea99ef4f14afc023711dd7c54ade5a" }, "fingers_crossed": { "category": "people", "moji": "🤞", + "description": "hand with first and index finger crossed", "unicodeVersion": "9.0", "digest": "a5c797ead191b9712e185083266b455cdf09f6a34c10f8c51aa145e6073427e1" }, "fingers_crossed_tone1": { "category": "people", "moji": "🤞🏻", + "description": "hand with index and middle fingers crossed tone 1", "unicodeVersion": "9.0", "digest": "db56d47bf887f2d8459a3aaba23f15c0087234ae5a54125052e7046e034a4988" }, "fingers_crossed_tone2": { "category": "people", "moji": "🤞🏼", + "description": "hand with index and middle fingers crossed tone 2", "unicodeVersion": "9.0", "digest": "19f1bcca3991db7ed2037278c0baab6cd7f12aeaf2e0074de402c4d9e45c1899" }, "fingers_crossed_tone3": { "category": "people", "moji": "🤞🏽", + "description": "hand with index and middle fingers crossed tone 3", "unicodeVersion": "9.0", "digest": "895a3314f6a310f31f7e728bcca20ff834fbfac62ce00e27e3ea5ad0dfc1ba35" }, "fingers_crossed_tone4": { "category": "people", "moji": "🤞🏾", + "description": "hand with index and middle fingers crossed tone 4", "unicodeVersion": "9.0", "digest": "fcb5c4de2001d23a5df1b8702624d134b7f94e93e2dcc8adf6c1033c77722b0e" }, "fingers_crossed_tone5": { "category": "people", "moji": "🤞🏿", + "description": "hand with index and middle fingers crossed tone 5", "unicodeVersion": "9.0", "digest": "50132c78d530b048c21be4e788b446872a79b3b3a91009db12f4021c44c8469d" }, "fire": { "category": "nature", "moji": "🔥", + "description": "fire", "unicodeVersion": "6.0", "digest": "b3e67c913903d900f5e50e7e7e4d7e9370bb6ceedfbee548be39e4c9e4b69416" }, "fire_engine": { "category": "travel", "moji": "🚒", + "description": "fire engine", "unicodeVersion": "6.0", "digest": "c3a518f27d625e3b62dffa227eb82764bf0a147f10ec0e7f4f43f3f96751af20" }, "fireworks": { "category": "travel", "moji": "🎆", + "description": "fireworks", "unicodeVersion": "6.0", "digest": "b62ae08a00c0cc6eba8f9666c8fd9946ce57c3cfc01fe99542a8690a4a566a65" }, "first_place": { "category": "activity", "moji": "🥇", + "description": "first place medal", "unicodeVersion": "9.0", "digest": "e3de5d9f14f05544dbee5965cc2baa20e7b417a488c8a18598979038860fd901" }, "first_quarter_moon": { "category": "nature", "moji": "🌓", + "description": "first quarter moon symbol", "unicodeVersion": "6.0", "digest": "a207ce93084448622a4a5c49c85c566a9fda6be7337c86a013eeb713fe47fd29" }, "first_quarter_moon_with_face": { "category": "nature", "moji": "🌛", + "description": "first quarter moon with face", "unicodeVersion": "6.0", "digest": "1d1f54a5075f2311bcc017c44898b9d8c58edc13b298d58c238fff9ab8ee2ef3" }, "fish": { "category": "nature", "moji": "🐟", + "description": "fish", "unicodeVersion": "6.0", "digest": "8f62f08fbeaf39694c19816b5c7d4f292017fe5bf9f8dd7e40f1630f5f83b28b" }, "fish_cake": { "category": "food", "moji": "🍥", + "description": "fish cake with swirl design", "unicodeVersion": "6.0", "digest": "5a6ca2100c8830927b22afa6f1d2fc821f5692cd23507fe5a776f6e085cbbfb2" }, "fishing_pole_and_fish": { "category": "activity", "moji": "🎣", + "description": "fishing pole and fish", "unicodeVersion": "6.0", "digest": "f8fb84eccceec88321b0a2a46f732ecfc378f787c19c27ac1327735f1ca9a48b" }, "fist": { "category": "people", "moji": "✊", + "description": "raised fist", "unicodeVersion": "6.0", "digest": "557f96d85615b8d78436bc67266115bfc8556c97c14f7909dfda1cf134e8344f" }, "fist_tone1": { "category": "people", "moji": "✊🏻", + "description": "raised fist tone 1", "unicodeVersion": "8.0", "digest": "6c1b946f9e01abc39b5085e24e8b6077fc0e34188e8daa30c6a3adddd387413e" }, "fist_tone2": { "category": "people", "moji": "✊🏼", + "description": "raised fist tone 2", "unicodeVersion": "8.0", "digest": "e9b9e1ec638dca4d5e1519bca7338f58cce2f2a282ee4c3581e8643166fc415f" }, "fist_tone3": { "category": "people", "moji": "✊🏽", + "description": "raised fist tone 3", "unicodeVersion": "8.0", "digest": "8c14d24055c143960b3d2a27fe23c55d2d3ac5f84f87e4e876616235e8698c7f" }, "fist_tone4": { "category": "people", "moji": "✊🏾", + "description": "raised fist tone 4", "unicodeVersion": "8.0", "digest": "923f034f481e952e6e5d1664588f99f79bd5416d4197b0ade6621f2669ce5765" }, "fist_tone5": { "category": "people", "moji": "✊🏿", + "description": "raised fist tone 5", "unicodeVersion": "8.0", "digest": "d691d2902216080916a29047e07d7a5bf2aed07e062067ca9d01cbf6fdf48c8d" }, "five": { "category": "symbols", "moji": "5️⃣", + "description": "keycap digit five", "unicodeVersion": "3.0", "digest": "8f03f62fdbf744ae49c8a60fbf715ebfccbd6b62d91148e0923907006f3c2726" }, "flag_ac": { "category": "flags", "moji": "🇦🇨", + "description": "ascension", "unicodeVersion": "6.0", "digest": "2e5c08535dc8ea96422d56a36b4fffc0b3bd2a13f2ab0d8dbd0e3a29bf3fc40c" }, "flag_ad": { "category": "flags", "moji": "🇦🇩", + "description": "andorra", "unicodeVersion": "6.0", "digest": "184fdcf790b8e2fd851b2b2b32f8636c595dd289734d12dc01ae4aa177e2043a" }, "flag_ae": { "category": "flags", "moji": "🇦🇪", + "description": "the united arab emirates", "unicodeVersion": "6.0", "digest": "4a3257a9ce118e97567e76280f24d60fb555f1bada2eb26a2442a47f9398d21e" }, "flag_af": { "category": "flags", "moji": "🇦🇫", + "description": "afghanistan", "unicodeVersion": "6.0", "digest": "0f6c719cac7ab3140694f6b580787ecdbf503e38f16de7ec5803f7d06a088ec3" }, "flag_ag": { "category": "flags", "moji": "🇦🇬", + "description": "antigua and barbuda", "unicodeVersion": "6.0", "digest": "92bf5a0e74564739862e9ba79331ffa656b7bae2ace0fc8dfd288984e4d510d4" }, "flag_ai": { "category": "flags", "moji": "🇦🇮", + "description": "anguilla", "unicodeVersion": "6.0", "digest": "aeaadc7ffafd8a1e01fdabc69d35f725d5f737b4c284a36191d96729f4e66e8f" }, "flag_al": { "category": "flags", "moji": "🇦🇱", + "description": "albania", "unicodeVersion": "6.0", "digest": "5ce7866d214d18c5f3438d480d14e77d104c4de679f0fdfca8cf0a44ce48eeea" }, "flag_am": { "category": "flags", "moji": "🇦🇲", + "description": "armenia", "unicodeVersion": "6.0", "digest": "b40f5705f0cf9ef0fa7ffff0b371c4099319001ce79f894c317912f4dc5de4c8" }, "flag_ao": { "category": "flags", "moji": "🇦🇴", + "description": "angola", "unicodeVersion": "6.0", "digest": "eab6fbc1824d6e3cd152e8ec1d82e1beaebe02b53b35c6f7a883b8548af02f3a" }, "flag_aq": { "category": "flags", "moji": "🇦🇶", + "description": "antarctica", "unicodeVersion": "6.0", "digest": "367f6677a683a5f0e7248ab3a8f46d06ba146a0fd75004c70bac0e913147cdaa" }, "flag_ar": { "category": "flags", "moji": "🇦🇷", + "description": "argentina", "unicodeVersion": "6.0", "digest": "f0dc466b3216957f2679d7208c2d7cf288448b0739b9270a7c5fa717577bdf25" }, "flag_as": { "category": "flags", "moji": "🇦🇸", + "description": "american samoa", "unicodeVersion": "6.0", "digest": "fcb7a865c7763c63b23485cc27207b99a3a8492e83d5b5ee2df259a9f68f77d6" }, "flag_at": { "category": "flags", "moji": "🇦🇹", + "description": "austria", "unicodeVersion": "6.0", "digest": "1d3d58e9abc034f9a093a94716eddf9811d54dfaf27969fd322b3809fac70217" }, "flag_au": { "category": "flags", "moji": "🇦🇺", + "description": "australia", "unicodeVersion": "6.0", "digest": "789563b64c71a5ad49078d335dc166ef614edb56d1e401885d32fb191c198fbd" }, "flag_aw": { "category": "flags", "moji": "🇦🇼", + "description": "aruba", "unicodeVersion": "6.0", "digest": "1504dc3fd8457b44fdf75c15e136dc46a13e8342d1f98949728cdc1238843e0c" }, "flag_ax": { "category": "flags", "moji": "🇦🇽", + "description": "åland islands", "unicodeVersion": "6.0", "digest": "e96fa3525f3be25016a4cf8428261735f3ed5fc9fe5b827b461746a3f08877bf" }, "flag_az": { "category": "flags", "moji": "🇦🇿", + "description": "azerbaijan", "unicodeVersion": "6.0", "digest": "12c366ac2c38b91314fb29056e09fa6e7417766cebde3045859cdb127549f4a2" }, "flag_ba": { "category": "flags", "moji": "🇧🇦", + "description": "bosnia and herzegovina", "unicodeVersion": "6.0", "digest": "0819ea3901510ac20c7f10e67e5f6c818210f17a362c1d12e299c41feb07f828" }, "flag_bb": { "category": "flags", "moji": "🇧🇧", + "description": "barbados", "unicodeVersion": "6.0", "digest": "cf32778a272ed6cbc8e783b59befd9b204009c69c61a425e148d867808b7fab9" }, "flag_bd": { "category": "flags", "moji": "🇧🇩", + "description": "bangladesh", "unicodeVersion": "6.0", "digest": "e6ed186644a874588e879513aec92f8107220dcdd14c766dee61f266ce045665" }, "flag_be": { "category": "flags", "moji": "🇧🇪", + "description": "belgium", "unicodeVersion": "6.0", "digest": "4d941011d15d9f6e755d6f7694884758baf17ac0691bf5d63700f8d6dbcdb948" }, "flag_bf": { "category": "flags", "moji": "🇧🇫", + "description": "burkina faso", "unicodeVersion": "6.0", "digest": "fcc57dbda9a86f725f558b6c6309484c97e65f1644aae4f9fb5e642681f6c2e0" }, "flag_bg": { "category": "flags", "moji": "🇧🇬", + "description": "bulgaria", "unicodeVersion": "6.0", "digest": "816c47ed96c36c90723da150645902ea8ba18b44757fdd776c7b3542cfecfb18" }, "flag_bh": { "category": "flags", "moji": "🇧🇭", + "description": "bahrain", "unicodeVersion": "6.0", "digest": "2cd5c21775a6e73f59d08c9ee0cedf4e8241e562eab939573501d47681987737" }, "flag_bi": { "category": "flags", "moji": "🇧🇮", + "description": "burundi", "unicodeVersion": "6.0", "digest": "2da82acbec5518360633c1b0b56d55a79b67237f67d92af5e5cd75a2f3bd550e" }, "flag_bj": { "category": "flags", "moji": "🇧🇯", + "description": "benin", "unicodeVersion": "6.0", "digest": "8fe8c34651eb4e28ab395261a5b72b6f37579535ed676d15de131914e19c0436" }, "flag_bl": { "category": "flags", "moji": "🇧🇱", + "description": "saint barthélemy", "unicodeVersion": "6.0", "digest": "d37f2a215ee7ef5b5ab62d2a0c87e90553b17c6ee310f803a71e9fd72db880e7" }, "flag_black": { "category": "objects", "moji": "🏴", + "description": "waving black flag", "unicodeVersion": "6.0", "digest": "3740bfc9bcb3b46b697b8b7c47ab2c3e95eca9dbcba12f2bf98a01302704f203" }, "flag_bm": { "category": "flags", "moji": "🇧🇲", + "description": "bermuda", "unicodeVersion": "6.0", "digest": "ccd21655573f3c955d616c5c7b1eac2be1d4772ff611648d6713ba55d9e4aa9b" }, "flag_bn": { "category": "flags", "moji": "🇧🇳", + "description": "brunei", "unicodeVersion": "6.0", "digest": "54330c3d7a37392e69098c213fd8c78f3faab4e7e5909c039188110422514228" }, "flag_bo": { "category": "flags", "moji": "🇧🇴", + "description": "bolivia", "unicodeVersion": "6.0", "digest": "32aff973b26f4f91ca19dddd7861b564da43cfbee87603d8c004f1111342366c" }, "flag_bq": { "category": "flags", "moji": "🇧🇶", + "description": "caribbean netherlands", "unicodeVersion": "6.0", "digest": "b1ebc959c43f706ca430d8633d9efaa9c60133871506b5f030b730cfb4c19e6f" }, "flag_br": { "category": "flags", "moji": "🇧🇷", + "description": "brazil", "unicodeVersion": "6.0", "digest": "64fb154d71fa34ff4838bc405f3e58a4102cf0cb49ca4b06fc3c7a6bf39671f0" }, "flag_bs": { "category": "flags", "moji": "🇧🇸", + "description": "the bahamas", "unicodeVersion": "6.0", "digest": "c4b07e5f652ab06ece95d3774ce8b1399a935f8a28d440cb13cc8bd0b9728ed5" }, "flag_bt": { "category": "flags", "moji": "🇧🇹", + "description": "bhutan", "unicodeVersion": "6.0", "digest": "901ddbd999dd89a87c1e1208b1470cb4e604a9bc023d0cbcdee64e1bc54079ba" }, "flag_bv": { "category": "flags", "moji": "🇧🇻", + "description": "bouvet island", "unicodeVersion": "6.0", "digest": "bbf6daa6174c6fbbbf541c8274f31b6757c3a16007c2687015ea041fd1e2c6b6" }, "flag_bw": { "category": "flags", "moji": "🇧🇼", + "description": "botswana", "unicodeVersion": "6.0", "digest": "05aa351bc04dc0fe2669441ab500e000d48b1f0d7ad9e885c7abfb898aa0eb3f" }, "flag_by": { "category": "flags", "moji": "🇧🇾", + "description": "belarus", "unicodeVersion": "6.0", "digest": "6eda3b87336ecf0aae4963986d86b916a055d8268c70520303288f235a93b0d9" }, "flag_bz": { "category": "flags", "moji": "🇧🇿", + "description": "belize", "unicodeVersion": "6.0", "digest": "d76ed945b1408558a30a99b8eed6712de968fc49fba1721b5660b8f48087e45a" }, "flag_ca": { "category": "flags", "moji": "🇨🇦", + "description": "canada", "unicodeVersion": "6.0", "digest": "2fd036047d89751c05de5577909b58347883bc89c3b7d90bec28ad4770a98ecd" }, "flag_cc": { "category": "flags", "moji": "🇨🇨", + "description": "cocos (keeling) islands", "unicodeVersion": "6.0", "digest": "837ba181a01c71f05d438d205efaaee99f93b2370c97b13e6132f99860323e36" }, "flag_cd": { "category": "flags", "moji": "🇨🇩", + "description": "the democratic republic of the congo", "unicodeVersion": "6.0", "digest": "318689274b4b3b58aed7fc1654127499a9da69bff1b83e592e86e69d167ce16f" }, "flag_cf": { "category": "flags", "moji": "🇨🇫", + "description": "central african republic", "unicodeVersion": "6.0", "digest": "06d6042849d3b7b217c2b18ba787aae449e8c7d2537e2e5974744ec196062228" }, "flag_cg": { "category": "flags", "moji": "🇨🇬", + "description": "the republic of the congo", "unicodeVersion": "6.0", "digest": "09f45d2dcb5a24d8349ef86e7405cc29ef3d65a908c0bff3221c3b4546547813" }, "flag_ch": { "category": "flags", "moji": "🇨🇭", + "description": "switzerland", "unicodeVersion": "6.0", "digest": "53d6d35aeeebb0b4b1ad858dc3691e649ac73d30b3be76f96d5fe9605fa99386" }, "flag_ci": { "category": "flags", "moji": "🇨🇮", + "description": "cote d'ivoire", "unicodeVersion": "6.0", "digest": "7d85a0c314b7397c9397a54ce2f3a4dc5f40d0234e586dbd8a541a8666f0f51e" }, "flag_ck": { "category": "flags", "moji": "🇨🇰", + "description": "cook islands", "unicodeVersion": "6.0", "digest": "c1aa105fe106ed09ed59a596859a0ce4e65a415c59f63df51961491cb947b136" }, "flag_cl": { "category": "flags", "moji": "🇨🇱", + "description": "chile", "unicodeVersion": "6.0", "digest": "0fffdad0d892f5c08aaa332af1ed2c228583d89a43190e979a3c3cb020d5a723" }, "flag_cm": { "category": "flags", "moji": "🇨🇲", + "description": "cameroon", "unicodeVersion": "6.0", "digest": "e9f55e41a1fd2735a82ad7a7ac39326a944cb20423ffba3608ac53a46036caad" }, "flag_cn": { "category": "flags", "moji": "🇨🇳", + "description": "china", "unicodeVersion": "6.0", "digest": "e2c8fee7e3bd51b13d6083d5bf344abe6b9b642e3cbb099d38b4ce341c99d890" }, "flag_co": { "category": "flags", "moji": "🇨🇴", + "description": "colombia", "unicodeVersion": "6.0", "digest": "51c60d0979bf8342eaff7cda9faf4b0dfab38efaf5ddf3717eb8f0e2a595b15f" }, "flag_cp": { "category": "flags", "moji": "🇨🇵", + "description": "clipperton island", "unicodeVersion": "6.0", "digest": "a0124683aa88cd7da886da70c65796c5ad84eb3751e356e9b2aa8ac249cf0bf9" }, "flag_cr": { "category": "flags", "moji": "🇨🇷", + "description": "costa rica", "unicodeVersion": "6.0", "digest": "907905971b219e617a34eef4839b0bd08d98f3480e2631bce523120dcef95196" }, "flag_cu": { "category": "flags", "moji": "🇨🇺", + "description": "cuba", "unicodeVersion": "6.0", "digest": "d88cea729dc9dbbbcadac0409ec561995f061b2280577c01c6c6b37de347f150" }, "flag_cv": { "category": "flags", "moji": "🇨🇻", + "description": "cape verde", "unicodeVersion": "6.0", "digest": "5ce97944adfce09e96387e6f872256482ac99ccbc60017c4d58ddd15b6fb67a7" }, "flag_cw": { "category": "flags", "moji": "🇨🇼", + "description": "curaçao", "unicodeVersion": "6.0", "digest": "a6fc31bd66ddc2ee8e7bde3aeabfe1c4ad00c9688abae234a541cc1236d68c1b" }, "flag_cx": { "category": "flags", "moji": "🇨🇽", + "description": "christmas island", "unicodeVersion": "6.0", "digest": "1261b32bfa22fa1441f5390ff499ac6b921d7ac59cc8acda3deb3a2beb4fb345" }, "flag_cy": { "category": "flags", "moji": "🇨🇾", + "description": "cyprus", "unicodeVersion": "6.0", "digest": "82b1baa05ecffa0ea1f9a83b518163cbd7910985a21955740520bb16b7bb624f" }, "flag_cz": { "category": "flags", "moji": "🇨🇿", + "description": "the czech republic", "unicodeVersion": "6.0", "digest": "a169b18968992a52299b67c24fba495e84de28dec2ebb947a08e0d615ac54a5a" }, "flag_de": { "category": "flags", "moji": "🇩🇪", + "description": "germany", "unicodeVersion": "6.0", "digest": "99d1906944966a188c72ae592362ed907e2a0bfe95263955c34a0941507b30c1" }, "flag_dg": { "category": "flags", "moji": "🇩🇬", + "description": "diego garcia", "unicodeVersion": "6.0", "digest": "dd45e1afe792fca57d4161434bf611bcb7170072d63e4a27fb9dcd6e8912621e" }, "flag_dj": { "category": "flags", "moji": "🇩🇯", + "description": "djibouti", "unicodeVersion": "6.0", "digest": "e90ba4e98fca71ff0ca5e65c28b911cc52f043428f375d8f954ecbd3b0c8f4dd" }, "flag_dk": { "category": "flags", "moji": "🇩🇰", + "description": "denmark", "unicodeVersion": "6.0", "digest": "65b3b5f31935a4969d81fedbb8279c7ad32da454d15c5eafcceba5d140927c77" }, "flag_dm": { "category": "flags", "moji": "🇩🇲", + "description": "dominica", "unicodeVersion": "6.0", "digest": "f6225ded6d2cfd6c182ab1a53b8c49dc9df195df11eb7ff27b15f5d3721ba0eb" }, "flag_do": { "category": "flags", "moji": "🇩🇴", + "description": "the dominican republic", "unicodeVersion": "6.0", "digest": "dc2ad6856cebbe47c5bd7f5dcf087e4f680d396b2d49440a9b71f0ad49fb8102" }, "flag_dz": { "category": "flags", "moji": "🇩🇿", + "description": "algeria", "unicodeVersion": "6.0", "digest": "ea69fffc4d545f9c0fcef6768257501952955ba4d274c9b81843229a1265c5ed" }, "flag_ea": { "category": "flags", "moji": "🇪🇦", + "description": "ceuta, melilla", "unicodeVersion": "6.0", "digest": "e63bfe15428c481dd23b569e7aaf0a76106e58a946995b4415a81097ecd53b7d" }, "flag_ec": { "category": "flags", "moji": "🇪🇨", + "description": "ecuador", "unicodeVersion": "6.0", "digest": "0cdabf85cd567047fda1d9a4508220cab829943a7c542c315078db0aac33edac" }, "flag_ee": { "category": "flags", "moji": "🇪🇪", + "description": "estonia", "unicodeVersion": "6.0", "digest": "6dc4e3377e8e2af3ff40cf940a914bc7840980b4a14e7da86954343f2b1025fe" }, "flag_eg": { "category": "flags", "moji": "🇪🇬", + "description": "egypt", "unicodeVersion": "6.0", "digest": "2ed6bc056015694d75993eb5ee3c1850921d5630681207b04dfbdb982ab346a2" }, "flag_eh": { "category": "flags", "moji": "🇪🇭", + "description": "western sahara", "unicodeVersion": "6.0", "digest": "72adb55943e4df99c00843c65463718609d937480f73dcf4a4451d46b9967a5e" }, "flag_er": { "category": "flags", "moji": "🇪🇷", + "description": "eritrea", "unicodeVersion": "6.0", "digest": "3fa59331eb5300c8c1f7b1f1bc15cfcfe688da6fa4a79341854598086a44eebc" }, "flag_es": { "category": "flags", "moji": "🇪🇸", + "description": "spain", "unicodeVersion": "6.0", "digest": "1fa1d5cb0a7e8b14aaec758b2e7bf49cdf8f3d09bbcc7dfd589053a432eeae25" }, "flag_et": { "category": "flags", "moji": "🇪🇹", + "description": "ethiopia", "unicodeVersion": "6.0", "digest": "72771decfb214394e4beb594e848ea590c3615800adbba24b5df4c5db6ee9617" }, "flag_eu": { "category": "flags", "moji": "🇪🇺", + "description": "european union", "unicodeVersion": "6.0", "digest": "4bfa1b2ef23764ead5ef7899806f93e13fd29a09c75e61431579a4116c836aa4" }, "flag_fi": { "category": "flags", "moji": "🇫🇮", + "description": "finland", "unicodeVersion": "6.0", "digest": "d0208cdd5b153a2865f9f674179c62871d4675abb0fb639fba88fcd62553f54e" }, "flag_fj": { "category": "flags", "moji": "🇫🇯", + "description": "fiji", "unicodeVersion": "6.0", "digest": "6c5ec41114af3846b093a418f6e2b5ff7a83cb72cecde75a7dc62e8cb6dcfe45" }, "flag_fk": { "category": "flags", "moji": "🇫🇰", + "description": "falkland islands", "unicodeVersion": "6.0", "digest": "c69ad641d53785deff5c3934b7dcfcd3dc32ffc31b6d3e799d0555b03c23fc15" }, "flag_fm": { "category": "flags", "moji": "🇫🇲", + "description": "micronesia", "unicodeVersion": "6.0", "digest": "1e29fb06b273f253c23a9e4aa8ff84bfe22cffb5fa158a0c6f4cdeabe0216990" }, "flag_fo": { "category": "flags", "moji": "🇫🇴", + "description": "faroe islands", "unicodeVersion": "6.0", "digest": "f4907d2f606f4f9d3bef06c6d38e8e88f2a148197b1573668866431a007afc2e" }, "flag_fr": { "category": "flags", "moji": "🇫🇷", + "description": "france", "unicodeVersion": "6.0", "digest": "5a1308ab3cbf6bffcab12588cf3325151a6c72990db7408c2b8605d89f94ed6e" }, "flag_ga": { "category": "flags", "moji": "🇬🇦", + "description": "gabon", "unicodeVersion": "6.0", "digest": "ddc32dee2976507be878ec3d3d2408632ca21bc434cd9f58db4f6ac9774a2db5" }, "flag_gb": { "category": "flags", "moji": "🇬🇧", + "description": "great britain", "unicodeVersion": "6.0", "digest": "6b3bb254d134870b02cb066b06e206f652638a915c84b8649ceb30ec67fbebde" }, "flag_gd": { "category": "flags", "moji": "🇬🇩", + "description": "grenada", "unicodeVersion": "6.0", "digest": "b6a210541ca22d816405f2a7d0d5241dc4d5488c8a36e15bd1e3063f9c41327f" }, "flag_ge": { "category": "flags", "moji": "🇬🇪", + "description": "georgia", "unicodeVersion": "6.0", "digest": "e9a5035b7a46b925737e7f7b0ae2419cc4af0e980fbee5bd916edeef13823367" }, "flag_gf": { "category": "flags", "moji": "🇬🇫", + "description": "french guiana", "unicodeVersion": "6.0", "digest": "ce1bcd8c303897c1c22c5994182f21240b4aa635f0d7ce9944f76cbdbf0e4956" }, "flag_gg": { "category": "flags", "moji": "🇬🇬", + "description": "guernsey", "unicodeVersion": "6.0", "digest": "a435aab3609533ab2d68acd97deba844bfb0fc27b2adac68668223011f23ae5d" }, "flag_gh": { "category": "flags", "moji": "🇬🇭", + "description": "ghana", "unicodeVersion": "6.0", "digest": "7cad43b40f69b9b00cc1b38036789ce774fd3d597c89f0bf38433847ea69be26" }, "flag_gi": { "category": "flags", "moji": "🇬🇮", + "description": "gibraltar", "unicodeVersion": "6.0", "digest": "70e9b17d18bf3e0e4d03f4f824323a57909416e4082ca9d8a0796a6959de4f07" }, "flag_gl": { "category": "flags", "moji": "🇬🇱", + "description": "greenland", "unicodeVersion": "6.0", "digest": "1963d8cca1c1f06b7536b7fb8f5a4782ac0bb05afdf6e481101bce45c58cdd4b" }, "flag_gm": { "category": "flags", "moji": "🇬🇲", + "description": "the gambia", "unicodeVersion": "6.0", "digest": "6c776a8daa3f4daa2597b0025aec06fc0a53aed262e845d4da3897cd7a89c6a1" }, "flag_gn": { "category": "flags", "moji": "🇬🇳", + "description": "guinea", "unicodeVersion": "6.0", "digest": "134cf7c839370d171ae80a72e5d18d32ea1967df19c191d1a4ea446d649e9558" }, "flag_gp": { "category": "flags", "moji": "🇬🇵", + "description": "guadeloupe", "unicodeVersion": "6.0", "digest": "be3e906b039ba4884053c78f4f14de9aa87c5573860ccb69ec766068ae3887c2" }, "flag_gq": { "category": "flags", "moji": "🇬🇶", + "description": "equatorial guinea", "unicodeVersion": "6.0", "digest": "d476059c4ab41f5a1ef88583087362a5bc57cede930126f37041d1546564ab70" }, "flag_gr": { "category": "flags", "moji": "🇬🇷", + "description": "greece", "unicodeVersion": "6.0", "digest": "b9fa9304647aaa08167a07858bb18d778dcc399375f86f580b8d4244794678bc" }, "flag_gs": { "category": "flags", "moji": "🇬🇸", + "description": "south georgia", "unicodeVersion": "6.0", "digest": "de33fbef6e294eb7af36e5b94d8ff573b354a4ff1ebdccf50ca528b86ed601d9" }, "flag_gt": { "category": "flags", "moji": "🇬🇹", + "description": "guatemala", "unicodeVersion": "6.0", "digest": "4160843e5d642df597c8423eb8e3b74deafe304f3d141c8a4d2fc07509e44832" }, "flag_gu": { "category": "flags", "moji": "🇬🇺", + "description": "guam", "unicodeVersion": "6.0", "digest": "3b0cb257ba5b1c3e15d9102410c5f7418da03372e91ce90513de25b9f45283e3" }, "flag_gw": { "category": "flags", "moji": "🇬🇼", + "description": "guinea-bissau", "unicodeVersion": "6.0", "digest": "bdf07a8f93c0f0a573af5f5361be404a3ba65b729c1a4c05b7632c03d85efc72" }, "flag_gy": { "category": "flags", "moji": "🇬🇾", + "description": "guyana", "unicodeVersion": "6.0", "digest": "b47d8c98b747556f827ad0d1169264eb68ecaf9d2fb76595e8c31866361cbfc6" }, "flag_hk": { "category": "flags", "moji": "🇭🇰", + "description": "hong kong", "unicodeVersion": "6.0", "digest": "8e5a54b2e4bd4f5182085299b9648062463da05d535cf0e46a7d9c58eaeb171f" }, "flag_hm": { "category": "flags", "moji": "🇭🇲", + "description": "heard island and mcdonald islands", "unicodeVersion": "6.0", "digest": "63c3e080c5e82a72c6d4cf5997ac823dc02184719ec59aadea6dd41b127abf22" }, "flag_hn": { "category": "flags", "moji": "🇭🇳", + "description": "honduras", "unicodeVersion": "6.0", "digest": "87c1d160db810b5ed208fb33add54f96c17b0f08d87b81f6f09429abf6ec93ac" }, "flag_hr": { "category": "flags", "moji": "🇭🇷", + "description": "croatia", "unicodeVersion": "6.0", "digest": "8b68112f79baea38565673acf4f1cb90675a5829ff17e4cf9415c928b62aed88" }, "flag_ht": { "category": "flags", "moji": "🇭🇹", + "description": "haiti", "unicodeVersion": "6.0", "digest": "05dbd548c310ef1ebd1724aa85d821f8320106b16ddbf1f6442ea37e4407d5e1" }, "flag_hu": { "category": "flags", "moji": "🇭🇺", + "description": "hungary", "unicodeVersion": "6.0", "digest": "5079f3d6f1459e6df8dda5c19d2367ead8f5a755b8874ac999bae58e3c9f47a7" }, "flag_ic": { "category": "flags", "moji": "🇮🇨", + "description": "canary islands", "unicodeVersion": "6.0", "digest": "8dcb18c4b75a60867a68d2f6edbf81e782aafb4b9a0404c8081f872dfe71e432" }, "flag_id": { "category": "flags", "moji": "🇮🇩", + "description": "indonesia", "unicodeVersion": "6.0", "digest": "1b0eb69a158ed3afe24be448d44751f95dcc5cbc7d1393a5753293f16ef0a66c" }, "flag_ie": { "category": "flags", "moji": "🇮🇪", + "description": "ireland", "unicodeVersion": "6.0", "digest": "5fc8c101ad7296224455f72f73c335aa4f676023b68645bafaf69087f69af390" }, "flag_il": { "category": "flags", "moji": "🇮🇱", + "description": "israel", "unicodeVersion": "6.0", "digest": "5aea4207415b7615dcdd69413705aefda700aefd0d27010cd0a0a338d879d9b8" }, "flag_im": { "category": "flags", "moji": "🇮🇲", + "description": "isle of man", "unicodeVersion": "6.0", "digest": "1ee9b3a5f1a52fc6d8369bfd81995fc0567e7a61deacd013701b3ec5fd64502e" }, "flag_in": { "category": "flags", "moji": "🇮🇳", + "description": "india", "unicodeVersion": "6.0", "digest": "202ede502f34d55d180726ac2f29141c6875516f1b3e7ee99f266b16c2fe4bfd" }, "flag_io": { "category": "flags", "moji": "🇮🇴", + "description": "british indian ocean territory", "unicodeVersion": "6.0", "digest": "dd45e1afe792fca57d4161434bf611bcb7170072d63e4a27fb9dcd6e8912621e" }, "flag_iq": { "category": "flags", "moji": "🇮🇶", + "description": "iraq", "unicodeVersion": "6.0", "digest": "bef294772b5ffccd6c061c19d60af66f61b248d78705faf347ade9ebfca2b46d" }, "flag_ir": { "category": "flags", "moji": "🇮🇷", + "description": "iran", "unicodeVersion": "6.0", "digest": "d4faca93577a5546330ab6a09252307e19fb420d89912c0b48ceb90bf409d48e" }, "flag_is": { "category": "flags", "moji": "🇮🇸", + "description": "iceland", "unicodeVersion": "6.0", "digest": "b2fc04226b274009b4d99d92bcb72b255b534b6fd4b76d82dce1575ad975a456" }, "flag_it": { "category": "flags", "moji": "🇮🇹", + "description": "italy", "unicodeVersion": "6.0", "digest": "735760f193855d55460a0fb93dad55ff67253cab63176eceb90b9bde1faead1e" }, "flag_je": { "category": "flags", "moji": "🇯🇪", + "description": "jersey", "unicodeVersion": "6.0", "digest": "671a487a60571d928d2abaf306d0a9ba50239ec54ada14ea29a9a99df658d3cc" }, "flag_jm": { "category": "flags", "moji": "🇯🇲", + "description": "jamaica", "unicodeVersion": "6.0", "digest": "fb9047199d030b78fc0dcfc58d9b524fdb929238d922809da88147b7cebf4211" }, "flag_jo": { "category": "flags", "moji": "🇯🇴", + "description": "jordan", "unicodeVersion": "6.0", "digest": "19f7d536d0293ebf3db49e05a158097cbde467115ef96523a0553808fd0b4178" }, "flag_jp": { "category": "flags", "moji": "🇯🇵", + "description": "japan", "unicodeVersion": "6.0", "digest": "51e971f777fe481ca9f7e077ecb2ce252c3cc0086b76384e7b965cdc337f3f9e" }, "flag_ke": { "category": "flags", "moji": "🇰🇪", + "description": "kenya", "unicodeVersion": "6.0", "digest": "0cec8f068548cfd3e7a20c10af84f97ca415fd6f8ab8b50783bf982e77d7260e" }, "flag_kg": { "category": "flags", "moji": "🇰🇬", + "description": "kyrgyzstan", "unicodeVersion": "6.0", "digest": "5803ea6ab028261923fd7570c670a50518c6f462a2fb4d463531b12c3e382e6f" }, "flag_kh": { "category": "flags", "moji": "🇰🇭", + "description": "cambodia", "unicodeVersion": "6.0", "digest": "287d357afe47179853fd485fb102834ead145598ed892664fc62d245cac16080" }, "flag_ki": { "category": "flags", "moji": "🇰🇮", + "description": "kiribati", "unicodeVersion": "6.0", "digest": "ae4aee0d9cd7a21d4e250d45a484f5f641acdab3d79b437337b25fe34a0b49b0" }, "flag_km": { "category": "flags", "moji": "🇰🇲", + "description": "the comoros", "unicodeVersion": "6.0", "digest": "2d1730acbf5421fd02bd5483e26a86d82ec2fa99f0ff75bfd728a9df7914ad3b" }, "flag_kn": { "category": "flags", "moji": "🇰🇳", + "description": "saint kitts and nevis", "unicodeVersion": "6.0", "digest": "b9ed979db9c6d243b00f61f19a9ec0f2c2390b2e5cace5ad61d9371dc8c670ac" }, "flag_kp": { "category": "flags", "moji": "🇰🇵", + "description": "north korea", "unicodeVersion": "6.0", "digest": "1bab0b9cab8028a95ce7231ad8d88ebcd31601cfa321284bba017ead47f6c729" }, "flag_kr": { "category": "flags", "moji": "🇰🇷", + "description": "korea", "unicodeVersion": "6.0", "digest": "33be8c09ebe273e203aa703cc827d52a6d9bf1699f5445bba13a77af2df45fa6" }, "flag_kw": { "category": "flags", "moji": "🇰🇼", + "description": "kuwait", "unicodeVersion": "6.0", "digest": "04d901a92ea55b13dc4983a9e3adb52dc89c9f3decee86fd06022aa902678b6d" }, "flag_ky": { "category": "flags", "moji": "🇰🇾", + "description": "cayman islands", "unicodeVersion": "6.0", "digest": "10f4d02f33cadd34da89de71a3b763809bad480cd9ae9d2ec000db026bd94cd1" }, "flag_kz": { "category": "flags", "moji": "🇰🇿", + "description": "kazakhstan", "unicodeVersion": "6.0", "digest": "dfaff69a78cf635f7fad41bd5bdcc8003298454708a6178ba7348b1b40c360c1" }, "flag_la": { "category": "flags", "moji": "🇱🇦", + "description": "laos", "unicodeVersion": "6.0", "digest": "4fcfbdc694cf99ae3f832500cdcdedb88c444b6df88bc9b7141f4f26ba3d5bfd" }, "flag_lb": { "category": "flags", "moji": "🇱🇧", + "description": "lebanon", "unicodeVersion": "6.0", "digest": "af4b1f784bea0ec7a712495491dffbd1152cc857a99fd433f76bfeb313819a62" }, "flag_lc": { "category": "flags", "moji": "🇱🇨", + "description": "saint lucia", "unicodeVersion": "6.0", "digest": "40784aa558b75d07ae499c004e2cc5d0b2efdfc3e5be705b5a9f6b70d681c396" }, "flag_li": { "category": "flags", "moji": "🇱🇮", + "description": "liechtenstein", "unicodeVersion": "6.0", "digest": "c4eb4c43f457ce60ff9d046adb512c1d3462203403eeb595bff3ebc010ed6633" }, "flag_lk": { "category": "flags", "moji": "🇱🇰", + "description": "sri lanka", "unicodeVersion": "6.0", "digest": "a5285cdfdc3715fa3941f5f0eb03dc425969eaaf22c719c27ab4418628d09bc5" }, "flag_lr": { "category": "flags", "moji": "🇱🇷", + "description": "liberia", "unicodeVersion": "6.0", "digest": "ed04334264953b4da570db8c392b99d2fab4e0b7efc2331427016c6a08e818be" }, "flag_ls": { "category": "flags", "moji": "🇱🇸", + "description": "lesotho", "unicodeVersion": "6.0", "digest": "cd56022106d027317cc9bf4c848758cf29ffe277ce71fdb9c1cf89ac4fd6e6db" }, "flag_lt": { "category": "flags", "moji": "🇱🇹", + "description": "lithuania", "unicodeVersion": "6.0", "digest": "3c4395b068e421100fd97a102f170cb8d5c093885eef7cb40d3faff4f4e47fe9" }, "flag_lu": { "category": "flags", "moji": "🇱🇺", + "description": "luxembourg", "unicodeVersion": "6.0", "digest": "df15a2c47eecad17e0cc169bdf0d31c6a51eb22de7ca4e70d2431359a33f930d" }, "flag_lv": { "category": "flags", "moji": "🇱🇻", + "description": "latvia", "unicodeVersion": "6.0", "digest": "9b53c6ce23287935200da8ca8a8af78013a4b1572f9821e7e1724cbad248e7e2" }, "flag_ly": { "category": "flags", "moji": "🇱🇾", + "description": "libya", "unicodeVersion": "6.0", "digest": "42efa9f3526ef006d6723fa17538a98ab9556ae25f14df1b06d21361bf7e1a44" }, "flag_ma": { "category": "flags", "moji": "🇲🇦", + "description": "morocco", "unicodeVersion": "6.0", "digest": "96c07296cfd7aa1cb642faed8ace26744105b81ca880157a4ef4caee0befe26e" }, "flag_mc": { "category": "flags", "moji": "🇲🇨", + "description": "monaco", "unicodeVersion": "6.0", "digest": "6b44608842fe849ae2b4bae5eb87ccd436459a427051dfda25080196273d4b9f" }, "flag_md": { "category": "flags", "moji": "🇲🇩", + "description": "moldova", "unicodeVersion": "6.0", "digest": "78c7b01c698873a9129d52ba38b3eb4cfc683ef2ae10b7b922b17c07f1c938c8" }, "flag_me": { "category": "flags", "moji": "🇲🇪", + "description": "montenegro", "unicodeVersion": "6.0", "digest": "01aa0f9df89302edc4ae319b5dd78069ba8807c3f38cc7bfe01bff67c8efd416" }, "flag_mf": { "category": "flags", "moji": "🇲🇫", + "description": "saint martin", "unicodeVersion": "6.0", "digest": "a0124683aa88cd7da886da70c65796c5ad84eb3751e356e9b2aa8ac249cf0bf9" }, "flag_mg": { "category": "flags", "moji": "🇲🇬", + "description": "madagascar", "unicodeVersion": "6.0", "digest": "56ebcd2a2e144d656d3b38a62595138fe6e50f9c1144f70b0a120cce7a72eb5b" }, "flag_mh": { "category": "flags", "moji": "🇲🇭", + "description": "the marshall islands", "unicodeVersion": "6.0", "digest": "008660adc4c2e4d04830498988184d1ef8a372a6c085da369a94ee6b820dbbb7" }, "flag_mk": { "category": "flags", "moji": "🇲🇰", + "description": "macedonia", "unicodeVersion": "6.0", "digest": "f3c4c5106ace81c21fc0c6a7cc5c5e04e9453468fbc6ccbc851bb8dd61ff237f" }, "flag_ml": { "category": "flags", "moji": "🇲🇱", + "description": "mali", "unicodeVersion": "6.0", "digest": "e70a6b30e46adc2e19684308a848fef2c3ad76e2cac4bb493ee3270ad39f9d1b" }, "flag_mm": { "category": "flags", "moji": "🇲🇲", + "description": "myanmar", "unicodeVersion": "6.0", "digest": "720f5d38887202ba049cd5a46c183679be6a01f169d99e6e656c73b515793a7d" }, "flag_mn": { "category": "flags", "moji": "🇲🇳", + "description": "mongolia", "unicodeVersion": "6.0", "digest": "5f0fd6fcb2ed73a5a6d9396c3703612503c1f16283bbb4e9362a1c8324b762ad" }, "flag_mo": { "category": "flags", "moji": "🇲🇴", + "description": "macau", "unicodeVersion": "6.0", "digest": "fc2a9e7323867cf195f551e59afdab778c56b84c96af28c20207c9870caa2c39" }, "flag_mp": { "category": "flags", "moji": "🇲🇵", + "description": "northern mariana islands", "unicodeVersion": "6.0", "digest": "ddce3be9d72914240c42e1b97ea97af01016d0a3879999cb0e447552682c06ba" }, "flag_mq": { "category": "flags", "moji": "🇲🇶", + "description": "martinique", "unicodeVersion": "6.0", "digest": "888f455b1322d6fb83dc9f469f5505fea3dd6ece77d17d0d7345319c3ebcec0e" }, "flag_mr": { "category": "flags", "moji": "🇲🇷", + "description": "mauritania", "unicodeVersion": "6.0", "digest": "72621914c92dd9c9f3ac9973ee3589583bfe42b841cdd35f47af75e2f629726c" }, "flag_ms": { "category": "flags", "moji": "🇲🇸", + "description": "montserrat", "unicodeVersion": "6.0", "digest": "5944996295132f41ec55261ff7927518bd47aec95d274a6ff257c357b43657bc" }, "flag_mt": { "category": "flags", "moji": "🇲🇹", + "description": "malta", "unicodeVersion": "6.0", "digest": "95f0550e8823441a4e69b26c540baea94f3ddcc282100fd0239021c00df0b469" }, "flag_mu": { "category": "flags", "moji": "🇲🇺", + "description": "mauritius", "unicodeVersion": "6.0", "digest": "5fda78a6df0ea7f5cac5fb4c8fd68529c14c5e15bac4e0b167493cb6ac459253" }, "flag_mv": { "category": "flags", "moji": "🇲🇻", + "description": "maldives", "unicodeVersion": "6.0", "digest": "f75c8f6fd3a68f2944a04c833c649d4b576997f491100cf3f3160fe77117fabb" }, "flag_mw": { "category": "flags", "moji": "🇲🇼", + "description": "malawi", "unicodeVersion": "6.0", "digest": "d46b484a97e5b90b6b259f8de1712b553f93f0dfb6391209200358bb9429ebf5" }, "flag_mx": { "category": "flags", "moji": "🇲🇽", + "description": "mexico", "unicodeVersion": "6.0", "digest": "dc57c10307fc0aa09bd7fcd25ee0fca561f3b382276faa8432a927c1baea53fd" }, "flag_my": { "category": "flags", "moji": "🇲🇾", + "description": "malaysia", "unicodeVersion": "6.0", "digest": "15ca00660a1eb0096fdaa00b85a7b95fcf192bf2ee4781ba72c36d2d2cb015ef" }, "flag_mz": { "category": "flags", "moji": "🇲🇿", + "description": "mozambique", "unicodeVersion": "6.0", "digest": "0c8605a9319dcf86672a833b4c4d6acea5f6aa25a3f8e1dfac78fbf7c452ba97" }, "flag_na": { "category": "flags", "moji": "🇳🇦", + "description": "namibia", "unicodeVersion": "6.0", "digest": "e63cde5ee49d3ada1e33d2ab15dc24fbb129b90d65b6fd1d7c07455f71a53601" }, "flag_nc": { "category": "flags", "moji": "🇳🇨", + "description": "new caledonia", "unicodeVersion": "6.0", "digest": "a4a350ce7404ba7bdda9a341e7a48fcfe16312be4964b1bd6eed7115acd2e329" }, "flag_ne": { "category": "flags", "moji": "🇳🇪", + "description": "niger", "unicodeVersion": "6.0", "digest": "6b32483b4445bc52855509f618c570b9c9606de5649e4878b71b44ff2acbc9fd" }, "flag_nf": { "category": "flags", "moji": "🇳🇫", + "description": "norfolk island", "unicodeVersion": "6.0", "digest": "96b1ec33acbd2b1ffe42703c11a2a633b036e6779849b0e6fa8f399167820584" }, "flag_ng": { "category": "flags", "moji": "🇳🇬", + "description": "nigeria", "unicodeVersion": "6.0", "digest": "f97d0630cbfa5e75440251df7529a67b58c22598643390cbeea82fb04a1cd956" }, "flag_ni": { "category": "flags", "moji": "🇳🇮", + "description": "nicaragua", "unicodeVersion": "6.0", "digest": "c52fb5f9134122a91defa75425be2c6b3c909e051d546244e0e7bdf5f9ee1710" }, "flag_nl": { "category": "flags", "moji": "🇳🇱", + "description": "the netherlands", "unicodeVersion": "6.0", "digest": "b8918f9c0c92513aa0ec6ba6cee5448270168cbe6f0a970fb06e7ceb9f52ec71" }, "flag_no": { "category": "flags", "moji": "🇳🇴", + "description": "norway", "unicodeVersion": "6.0", "digest": "05ce84095f8d93407d611b39d8b6a67fd9f11df6cfab7a185bcb4eec186d85ef" }, "flag_np": { "category": "flags", "moji": "🇳🇵", + "description": "nepal", "unicodeVersion": "6.0", "digest": "cc41c2f97ec2b38fe5781d553792f6aab5d37cc3be02586f361fe89d12683bee" }, "flag_nr": { "category": "flags", "moji": "🇳🇷", + "description": "nauru", "unicodeVersion": "6.0", "digest": "7837edf59ec33a25380d76afea5f04cfcab4f17df4e33fca0dcaacb517c5cbec" }, "flag_nu": { "category": "flags", "moji": "🇳🇺", + "description": "niue", "unicodeVersion": "6.0", "digest": "fd9ab45c6f32bc4da47542392e5beba73ddac302a4a9a00e6deedc913a4c087d" }, "flag_nz": { "category": "flags", "moji": "🇳🇿", + "description": "new zealand", "unicodeVersion": "6.0", "digest": "0719830dcca400cefb30ce399bb03f49dd84c9a98f7d6a28270f9278e2a7af75" }, "flag_om": { "category": "flags", "moji": "🇴🇲", + "description": "oman", "unicodeVersion": "6.0", "digest": "3f9039becd52e3454fdf7611cdb0d7fb1196e053eea29ef87daab6c21a94f1ee" }, "flag_pa": { "category": "flags", "moji": "🇵🇦", + "description": "panama", "unicodeVersion": "6.0", "digest": "1adf0e5d4084e072aa44bd9978829e77546e0be75785e9be69f92e326bd714a7" }, "flag_pe": { "category": "flags", "moji": "🇵🇪", + "description": "peru", "unicodeVersion": "6.0", "digest": "f8a4e257676f4ab8962ffe5509b8417777a8be2f0e9dc7735d3e014ff221aab1" }, "flag_pf": { "category": "flags", "moji": "🇵🇫", + "description": "french polynesia", "unicodeVersion": "6.0", "digest": "1ace6cc71d130cdf09246297740a911f14828c322e35330cc548ca5975015c23" }, "flag_pg": { "category": "flags", "moji": "🇵🇬", + "description": "papua new guinea", "unicodeVersion": "6.0", "digest": "9c37719d9f51ef31fec0f898d38e522b4253cd00344408e3f660132514efddb7" }, "flag_ph": { "category": "flags", "moji": "🇵🇭", + "description": "the philippines", "unicodeVersion": "6.0", "digest": "f1af628cf6d1d290cedef3d564b2386e2d6f14ba4426d3fefc0312cb8772e517" }, "flag_pk": { "category": "flags", "moji": "🇵🇰", + "description": "pakistan", "unicodeVersion": "6.0", "digest": "61c77f73d2a10a5acb289fadfe0d25d1a1c343e1223bd802099ff4e0e9356521" }, "flag_pl": { "category": "flags", "moji": "🇵🇱", + "description": "poland", "unicodeVersion": "6.0", "digest": "38c2c8618446e1f72cf983ab33e736d943f0db7c4cce52a187299e8cec2ea895" }, "flag_pm": { "category": "flags", "moji": "🇵🇲", + "description": "saint pierre and miquelon", "unicodeVersion": "6.0", "digest": "656be9ea1a79c3885a759c7ce353d338345a198d7939556949affaf5490cb644" }, "flag_pn": { "category": "flags", "moji": "🇵🇳", + "description": "pitcairn", "unicodeVersion": "6.0", "digest": "2792260d8087ab0253b1214c1420f0160ab2eef9afe7315f9e7ff0b87cd15d72" }, "flag_pr": { "category": "flags", "moji": "🇵🇷", + "description": "puerto rico", "unicodeVersion": "6.0", "digest": "c4cfa1f2201dcda9de310a8247e6ce32d2798ae426a14dd70a9ebb00a2804d46" }, "flag_ps": { "category": "flags", "moji": "🇵🇸", + "description": "palestinian authority", "unicodeVersion": "6.0", "digest": "197f2ec6294bf0ee4a08cf2f2d1e237ee867c98b3085454a3f42abc955eeb289" }, "flag_pt": { "category": "flags", "moji": "🇵🇹", + "description": "portugal", "unicodeVersion": "6.0", "digest": "86a50827963756b5bf471ed9df5b3f2a2058b4c5d778a303414b6b0556e2082b" }, "flag_pw": { "category": "flags", "moji": "🇵🇼", + "description": "palau", "unicodeVersion": "6.0", "digest": "a6321c47a0cd188fbfdf3b55f17a7170c63080d28d50e4f5463eb1ee09af2412" }, "flag_py": { "category": "flags", "moji": "🇵🇾", + "description": "paraguay", "unicodeVersion": "6.0", "digest": "1a169e8d8703c510c5a2265b57dbed2f811b03ec375bcb341ab4cd0b100a9dd6" }, "flag_qa": { "category": "flags", "moji": "🇶🇦", + "description": "qatar", "unicodeVersion": "6.0", "digest": "de6283965cd98a244b7fa6288174f9ff0d8feb497f191f2e4ab3b690138a3d5d" }, "flag_re": { "category": "flags", "moji": "🇷🇪", + "description": "réunion", "unicodeVersion": "6.0", "digest": "260e1b97abc1562e5a73d7e53652ffed8059fc9b1c969741c466f48ec6ab0e80" }, "flag_ro": { "category": "flags", "moji": "🇷🇴", + "description": "romania", "unicodeVersion": "6.0", "digest": "6d648e03955fa2a9fd2bad6f60ec96d3e20ee57f5855f3721a4d4e0c8e99f95c" }, "flag_rs": { "category": "flags", "moji": "🇷🇸", + "description": "serbia", "unicodeVersion": "6.0", "digest": "95cd5e197ed364e403eeb7f1d18a83487d89166910ba8119ea994e5e19d6a7ee" }, "flag_ru": { "category": "flags", "moji": "🇷🇺", + "description": "russia", "unicodeVersion": "6.0", "digest": "a4a81617a59d9eaf3c526431ca6f90ed334a7c1f516bf70cbd3f1fdc6e6103d7" }, "flag_rw": { "category": "flags", "moji": "🇷🇼", + "description": "rwanda", "unicodeVersion": "6.0", "digest": "7a369f60db0876ffef111c319a3e8c9eaed620c875c51b98ed9ad5207b836dca" }, "flag_sa": { "category": "flags", "moji": "🇸🇦", + "description": "saudi arabia", "unicodeVersion": "6.0", "digest": "b249fbfd7ed415943f60bbd841965cf721979f960ccbe09396aebac1eca913d7" }, "flag_sb": { "category": "flags", "moji": "🇸🇧", + "description": "the solomon islands", "unicodeVersion": "6.0", "digest": "526b411260024ea7b6ea6c47f2549345c6cc6960e9a29bfa9aaec0772664d2dc" }, "flag_sc": { "category": "flags", "moji": "🇸🇨", + "description": "the seychelles", "unicodeVersion": "6.0", "digest": "d036b0d068745926120eaf746fa2e4433306e2e14c6b540d0cd6265e34471056" }, "flag_sd": { "category": "flags", "moji": "🇸🇩", + "description": "sudan", "unicodeVersion": "6.0", "digest": "889615bdb9b1f9c59c5f83ed4d22d54a0ed5dd5de263e729c58544cb06c55885" }, "flag_se": { "category": "flags", "moji": "🇸🇪", + "description": "sweden", "unicodeVersion": "6.0", "digest": "f471d80cfff340960a752c8c152ed4fb482df2a3712b0a56dfab31b9b806926a" }, "flag_sg": { "category": "flags", "moji": "🇸🇬", + "description": "singapore", "unicodeVersion": "6.0", "digest": "82f58a09f98593cc87e545f7e5c03d2aedaf82e54e73f71f58c18e994c3085ac" }, "flag_sh": { "category": "flags", "moji": "🇸🇭", + "description": "saint helena", "unicodeVersion": "6.0", "digest": "53914b1fa8c1b4f30bae6c1f6717f138fb4dbf482c3e20e33f7aea4ecfc0438d" }, "flag_si": { "category": "flags", "moji": "🇸🇮", + "description": "slovenia", "unicodeVersion": "6.0", "digest": "65d491daa69f9a11cec9ccc4df3a669f12ef95a5c312137776d4472719940ba3" }, "flag_sj": { "category": "flags", "moji": "🇸🇯", + "description": "svalbard and jan mayen", "unicodeVersion": "6.0", "digest": "bbf6daa6174c6fbbbf541c8274f31b6757c3a16007c2687015ea041fd1e2c6b6" }, "flag_sk": { "category": "flags", "moji": "🇸🇰", + "description": "slovakia", "unicodeVersion": "6.0", "digest": "d4fd03eca5bd3c9fb324ee04fae37c9a2d852bac8335369e3e720ef9b98fff36" }, "flag_sl": { "category": "flags", "moji": "🇸🇱", + "description": "sierra leone", "unicodeVersion": "6.0", "digest": "1455c98c11c248623d82be5484ab1c4dcd1dae449adc393eb1aa2d8c74aa3f02" }, "flag_sm": { "category": "flags", "moji": "🇸🇲", + "description": "san marino", "unicodeVersion": "6.0", "digest": "daec5864ac50c625d7bf49d6c1a170a094cf0d1b9a0bdf62a62406e7ec500a94" }, "flag_sn": { "category": "flags", "moji": "🇸🇳", + "description": "senegal", "unicodeVersion": "6.0", "digest": "4e4d43c467e5eb84c70f535f37f4f468319bd4b06c6ec3db3b54f69efdafd334" }, "flag_so": { "category": "flags", "moji": "🇸🇴", + "description": "somalia", "unicodeVersion": "6.0", "digest": "c1434dca361563a8e3ba88f1ad19c3f6c9cbb8f3ebc17ce128fde2351ff67d0c" }, "flag_sr": { "category": "flags", "moji": "🇸🇷", + "description": "suriname", "unicodeVersion": "6.0", "digest": "f3c6bfee2a052f03d56ba917b88595450cef111ffa9e92c7f39ef8c3c3bd12d1" }, "flag_ss": { "category": "flags", "moji": "🇸🇸", + "description": "south sudan", "unicodeVersion": "6.0", "digest": "c0ed7e4f41206f5363e8ebdc6c3f28080e2f07d99e6fb73c1f6226d83310e69d" }, "flag_st": { "category": "flags", "moji": "🇸🇹", + "description": "sao tome and principe", "unicodeVersion": "6.0", "digest": "b022ae5d6885e28c6e9c83c17dd0c24c731d4f3d5773c49051768cdd4df51330" }, "flag_sv": { "category": "flags", "moji": "🇸🇻", + "description": "el salvador", "unicodeVersion": "6.0", "digest": "5bafdd04d243ee3f3998f4ec0a3d03ff5a3975e771b1f94f89d7713193d7a242" }, "flag_sx": { "category": "flags", "moji": "🇸🇽", + "description": "sint maarten", "unicodeVersion": "6.0", "digest": "fb92e9f514bcc2f7abbd4e146edde50f030c940c833f184618cbb48e56af22bd" }, "flag_sy": { "category": "flags", "moji": "🇸🇾", + "description": "syria", "unicodeVersion": "6.0", "digest": "ee330da644d4ce1fdba98be5eaab5054aed8d91a34ab617199a4b2b77f62a10b" }, "flag_sz": { "category": "flags", "moji": "🇸🇿", + "description": "swaziland", "unicodeVersion": "6.0", "digest": "7fe0c7429efd9682cc39e57f4bba8d1491d301643ba999d57c4e1bc37517ed64" }, "flag_ta": { "category": "flags", "moji": "🇹🇦", + "description": "tristan da cunha", "unicodeVersion": "6.0", "digest": "b47e245a2708072a4dbaf190c9606baa4daf02e51627eeae6f20c3b4c95024c0" }, "flag_tc": { "category": "flags", "moji": "🇹🇨", + "description": "turks and caicos islands", "unicodeVersion": "6.0", "digest": "18cfff14c2503b9d24c91c668583d4a14efb17657d800eca86ae49b547c9da5c" }, "flag_td": { "category": "flags", "moji": "🇹🇩", + "description": "chad", "unicodeVersion": "6.0", "digest": "73d1db3365736915c4cdf9ba9343d9fd78962203b60334e8f3724d4b330b17db" }, "flag_tf": { "category": "flags", "moji": "🇹🇫", + "description": "french southern territories", "unicodeVersion": "6.0", "digest": "3bffeb4bc9ceb9cbb150de88e957b6e46509862ca7d616d5693124af084eb435" }, "flag_tg": { "category": "flags", "moji": "🇹🇬", + "description": "togo", "unicodeVersion": "6.0", "digest": "eb13a0e85baf73326f3ae3bc75e8406eca42000d7e42b0641120e64c0ab7ebaa" }, "flag_th": { "category": "flags", "moji": "🇹🇭", + "description": "thailand", "unicodeVersion": "6.0", "digest": "a4e42efa4bb94e90f3a92ae9ce14affaacd3a142c1e0da40d8cc839500e771fd" }, "flag_tj": { "category": "flags", "moji": "🇹🇯", + "description": "tajikistan", "unicodeVersion": "6.0", "digest": "ff926fa3e86e095683a61c4754355a5b4dd0ecb74393306bd791d130fd1a909d" }, "flag_tk": { "category": "flags", "moji": "🇹🇰", + "description": "tokelau", "unicodeVersion": "6.0", "digest": "3fa732d457ded6c83cd5f73d934f64c4e687eb0cde7c157d2fdcdccaf3b5fb52" }, "flag_tl": { "category": "flags", "moji": "🇹🇱", + "description": "east timor", "unicodeVersion": "6.0", "digest": "0ec2a4d22fb832060693089e518bbe370a4e13bfc28748f110fc13726409f473" }, "flag_tm": { "category": "flags", "moji": "🇹🇲", + "description": "turkmenistan", "unicodeVersion": "6.0", "digest": "b4724aa7ad13352f16a0936e61cbb85f0bd147583fc66597aff7e8ee7cf19c21" }, "flag_tn": { "category": "flags", "moji": "🇹🇳", + "description": "tunisia", "unicodeVersion": "6.0", "digest": "5ab308ffdde40f504d6ee080817bbddbe4f3f4ddb71f508c75e0144a8c8044d9" }, "flag_to": { "category": "flags", "moji": "🇹🇴", + "description": "tonga", "unicodeVersion": "6.0", "digest": "75b7e7198fa42f87986882b8ca251a229afcaa0a1188ae7b9f5ece87dc31a723" }, "flag_tr": { "category": "flags", "moji": "🇹🇷", + "description": "turkey", "unicodeVersion": "6.0", "digest": "9cc48a8f8fa9c17c1627272f68d4740da0e7ce17a2cf8c6b5c08cc9b95e1390c" }, "flag_tt": { "category": "flags", "moji": "🇹🇹", + "description": "trinidad and tobago", "unicodeVersion": "6.0", "digest": "f9e63543121bb3cd2e41bc7b0c2c4ba662bc1cc0520b79fc4e201ec6456fdf59" }, "flag_tv": { "category": "flags", "moji": "🇹🇻", + "description": "tuvalu", "unicodeVersion": "6.0", "digest": "6431e5f06cc7995ae7208c429ecf39339b545854cb6d6b7447f465fe53614dfc" }, "flag_tw": { "category": "flags", "moji": "🇹🇼", + "description": "the republic of china", "unicodeVersion": "6.0", "digest": "8395ab3c6a595023b006518a5345ac3612f2893d3a8f011b7e5802414236b03c" }, "flag_tz": { "category": "flags", "moji": "🇹🇿", + "description": "tanzania", "unicodeVersion": "6.0", "digest": "716181733cd9ac7a8f51a9a64bc5d21020e8112f6768e8c49c4d651a3ee0b8a4" }, "flag_ua": { "category": "flags", "moji": "🇺🇦", + "description": "ukraine", "unicodeVersion": "6.0", "digest": "304570736345e28734f5ff84a2b0481c2bb00bf29d9892bd749b57dec7741e30" }, "flag_ug": { "category": "flags", "moji": "🇺🇬", + "description": "uganda", "unicodeVersion": "6.0", "digest": "a1bafb74c54ee8c92cb025b55aebdb6081eec3fda6a7f86f2ee14d1b801a8e9c" }, "flag_um": { "category": "flags", "moji": "🇺🇲", + "description": "united states minor outlying islands", "unicodeVersion": "6.0", "digest": "b3c9ac72211f481f50cde09e10b92aa03b1ea90abf85418e60a35b84963273ee" }, "flag_us": { "category": "flags", "moji": "🇺🇸", + "description": "united states", "unicodeVersion": "6.0", "digest": "da79f9af0a188178a82e7dc3a62298fa416f4cfbcae432838df1abebca5c0d63" }, "flag_uy": { "category": "flags", "moji": "🇺🇾", + "description": "uruguay", "unicodeVersion": "6.0", "digest": "8348e901d775722497ee911c9c9b4bd767710760c507630a67ecb6d47cc646c7" }, "flag_uz": { "category": "flags", "moji": "🇺🇿", + "description": "uzbekistan", "unicodeVersion": "6.0", "digest": "2a1dc1e9469e01c58ea91f545ef3fe0bdfe5544a73a80407f8960d01b1e5db5c" }, "flag_va": { "category": "flags", "moji": "🇻🇦", + "description": "the vatican city", "unicodeVersion": "6.0", "digest": "0e8134ec94bff032bfc63b0b08587d5298c9b7f31edd5a5b35633ae911434e61" }, "flag_vc": { "category": "flags", "moji": "🇻🇨", + "description": "saint vincent and the grenadines", "unicodeVersion": "6.0", "digest": "e0290e1be72c8939ee6c398f00a107703b21b97d91b9bf465e553ffbf00304a7" }, "flag_ve": { "category": "flags", "moji": "🇻🇪", + "description": "venezuela", "unicodeVersion": "6.0", "digest": "76a6a6c2353def1f984d1a6980831e63f3aea5af2201b574197834e7c203d57a" }, "flag_vg": { "category": "flags", "moji": "🇻🇬", + "description": "british virgin islands", "unicodeVersion": "6.0", "digest": "56fc9317b8dd62cccc60010819f8b895dd4569a9b06368a9250f815c39177b8a" }, "flag_vi": { "category": "flags", "moji": "🇻🇮", + "description": "u.s. virgin islands", "unicodeVersion": "6.0", "digest": "2526a3e13b8ccd301f0763580430898c227bd209e3ce482c7951140b28948375" }, "flag_vn": { "category": "flags", "moji": "🇻🇳", + "description": "vietnam", "unicodeVersion": "6.0", "digest": "0cf6b9896bbe4da8ed7718d0abfd56cef1a8321e26f89d3ad1b48488eaffb7a5" }, "flag_vu": { "category": "flags", "moji": "🇻🇺", + "description": "vanuatu", "unicodeVersion": "6.0", "digest": "9dfa282ce1aafc62beacab76e1fc19a141c8bdeaa30898f69b083067b775d362" }, "flag_wf": { "category": "flags", "moji": "🇼🇫", + "description": "wallis and futuna", "unicodeVersion": "6.0", "digest": "a0124683aa88cd7da886da70c65796c5ad84eb3751e356e9b2aa8ac249cf0bf9" }, "flag_white": { "category": "objects", "moji": "🏳", + "description": "waving white flag", "unicodeVersion": "6.0", "digest": "d9be4b7ceb8309c48f88cfd07a9f7ce6758ea6e620e73293cf14baec03ca381c" }, "flag_ws": { "category": "flags", "moji": "🇼🇸", + "description": "samoa", "unicodeVersion": "6.0", "digest": "53addd0dc304a3c8893389ed227986ef2431828b8c071926aa09f9efd815b649" }, "flag_xk": { "category": "flags", "moji": "🇽🇰", + "description": "kosovo", "unicodeVersion": "6.0", "digest": "eba1a832e489e1c2734e773e685df5d128271fa5559d23c060e68be067bf6469" }, "flag_ye": { "category": "flags", "moji": "🇾🇪", + "description": "yemen", "unicodeVersion": "6.0", "digest": "edfa14266785042b6d5fe0f64fafa630b16a3ee7d010501de7cc8554c959afb0" }, "flag_yt": { "category": "flags", "moji": "🇾🇹", + "description": "mayotte", "unicodeVersion": "6.0", "digest": "472ebc676b5d31dec2ac5e02ce69014a3dd94609d30a95f39f3a752f49c85e8b" }, "flag_za": { "category": "flags", "moji": "🇿🇦", + "description": "south africa", "unicodeVersion": "6.0", "digest": "dad162942a43392b4cff6929bd5cbf58c382a03dbc0e552f03c07ad2d8ff08ce" }, "flag_zm": { "category": "flags", "moji": "🇿🇲", + "description": "zambia", "unicodeVersion": "6.0", "digest": "1521ecaf1d1fdc8c15f0c96a6b04e6d4050f26f943a826b3d3d661f6ded6d438" }, "flag_zw": { "category": "flags", "moji": "🇿🇼", + "description": "zimbabwe", "unicodeVersion": "6.0", "digest": "46d05b597c5c77c8e2dc7bd6d8dd62ebca01bc9c9dc9915dafe694ca56402825" }, "flags": { "category": "objects", "moji": "🎏", + "description": "carp streamer", "unicodeVersion": "6.0", "digest": "f860aa4df587cf140c3e9735bbd101e9fd5a1bfcea42e420d85ac0a9877fa21d" }, "flashlight": { "category": "objects", "moji": "🔦", + "description": "electric torch", "unicodeVersion": "6.0", "digest": "e929bbe76e0fd2dc5bd6476858a0bbc717fd21467710435d35d80efb38033d73" }, "fleur-de-lis": { "category": "symbols", "moji": "⚜", + "description": "fleur-de-lis", "unicodeVersion": "4.1", "digest": "ebf49007f367dc05580e9dab942e93e9dda12fa1dc2caa410ac7f8d8cd55d2a3" }, "floppy_disk": { "category": "objects", "moji": "💾", + "description": "floppy disk", "unicodeVersion": "6.0", "digest": "4ee0b5bba41b9e301ed125d3ee1c263bef171ca499e6e1b89276b09af2bc03a0" }, "flower_playing_cards": { "category": "symbols", "moji": "🎴", + "description": "flower playing cards", "unicodeVersion": "6.0", "digest": "edba47c2e3051b2c7effd98794ec977174052782edcb491daec82a2b0d853869" }, "flushed": { "category": "people", "moji": "😳", + "description": "flushed face", "unicodeVersion": "6.0", "digest": "e759d46bab92af5494d78b6c712c06568759afe397e7828ca0a0de1e3eab0165" }, "fog": { "category": "nature", "moji": "🌫", + "description": "fog", "unicodeVersion": "7.0", "digest": "0cbd4733961d30fe0f40f95dd1f37254aebbef26f82dd18ad2000e799eb2898e" }, "foggy": { "category": "travel", "moji": "🌁", + "description": "foggy", "unicodeVersion": "6.0", "digest": "bc3631a4e9e8473b92e842008937add2cd9ffad5b7d772ce759fb5ff6c0e3dca" }, "football": { "category": "activity", "moji": "🏈", + "description": "american football", "unicodeVersion": "6.0", "digest": "ebd790471c3a28d3077818e3b31d915ffe443e06e299bc5cf0dd2534d080634c" }, "footprints": { "category": "people", "moji": "👣", + "description": "footprints", "unicodeVersion": "6.0", "digest": "85bbf2bc0ae8e6259d83a06f513600095d7fcfc44372670f5b2405d380b78811" }, "fork_and_knife": { "category": "food", "moji": "🍴", + "description": "fork and knife", "unicodeVersion": "6.0", "digest": "f228accd36ddccb4ec636207c19d7185191ec79723b780a1bd5c3d00a4b1ef3b" }, "fork_knife_plate": { "category": "food", "moji": "🍽", + "description": "fork and knife with plate", "unicodeVersion": "7.0", "digest": "ec6be99dac8efd3d145807fa60d2b6d8f6d3c02cb95552b55cc0fac39a4db48e" }, "fountain": { "category": "travel", "moji": "⛲", + "description": "fountain", "unicodeVersion": "5.2", "digest": "87043f9256e1d4615159307fcfd21bf6ae2aba0bada7de2bd50d7d6f2ab82395" }, "four": { "category": "symbols", "moji": "4️⃣", + "description": "keycap digit four", "unicodeVersion": "3.0", "digest": "c2c82a966bbb599aae557d930a4fc42604f2081aa45528872f5caf4942ee79d9" }, "four_leaf_clover": { "category": "nature", "moji": "🍀", + "description": "four leaf clover", "unicodeVersion": "6.0", "digest": "ebee16e86bc9be843dfc72ab5372fb462f06be4486b5b25d7d4cac9b2c8b01c8" }, "fox": { "category": "nature", "moji": "🦊", + "description": "fox face", "unicodeVersion": "9.0", "digest": "e9903cb0396f7e49bdd2c384b38e614c13bfa576b3ecc1ec7b9819e4a40d91d1" }, "frame_photo": { "category": "objects", "moji": "🖼", + "description": "frame with picture", "unicodeVersion": "7.0", "digest": "d5074f748a15055ec1fb812c1e5e169e6e3cc73c522c54be1359b0e26c0fc75c" }, "free": { "category": "symbols", "moji": "🆓", + "description": "squared free", "unicodeVersion": "6.0", "digest": "9973522457158362fc5bdd7da858e6371e28a8403d1ef9e4b6427195c7f72cfa" }, "french_bread": { "category": "food", "moji": "🥖", + "description": "baguette bread", "unicodeVersion": "9.0", "digest": "47518a4312f57207b8e8c38188d4a2bd8b16830a885cfcf2d281cfab50c1bc6e" }, "fried_shrimp": { "category": "food", "moji": "🍤", + "description": "fried shrimp", "unicodeVersion": "6.0", "digest": "0792bdc4484852de970c8f43bc3a1a339dc0e48090ec77d6de97cbfcdd17f9e1" }, "fries": { "category": "food", "moji": "🍟", + "description": "french fries", "unicodeVersion": "6.0", "digest": "47915aea67251d358d91a0e4dc3dcc347155336007d6b931a192be72a743b4e9" }, "frog": { "category": "nature", "moji": "🐸", + "description": "frog face", "unicodeVersion": "6.0", "digest": "d024b2ce771df64040534fb0906737d18b562bc3578dee62c2f25ec03c7caffd" }, "frowning": { "category": "people", "moji": "😦", + "description": "frowning face with open mouth", "unicodeVersion": "6.1", "digest": "c01af48537b0011d313d8f65103e1401fce4f5c0269c68e0e9806926c59acc44" }, "frowning2": { "category": "people", "moji": "☹", + "description": "white frowning face", "unicodeVersion": "1.1", "digest": "6568ee393b950c852d440112e86908c456b89fb7780e27778c5fcec168373fbf" }, "fuelpump": { "category": "travel", "moji": "⛽", + "description": "fuel pump", "unicodeVersion": "5.2", "digest": "105e736469f19911b8bab4ab6d29f949ded4b061b54e3dd763726577d6453095" }, "full_moon": { "category": "nature", "moji": "🌕", + "description": "full moon symbol", "unicodeVersion": "6.0", "digest": "aaa87f4676a5aaa29c1b721a3b582e89db6c1f35a25c52e4b480bd193ef39c43" }, "full_moon_with_face": { "category": "nature", "moji": "🌝", + "description": "full moon with face", "unicodeVersion": "6.0", "digest": "05c4b9c339fcdf81ae67027641522baa99c370d87873ff4c8133b8349e627e33" }, "game_die": { "category": "activity", "moji": "🎲", + "description": "game die", "unicodeVersion": "6.0", "digest": "00d19ce8e21dba2cdfeb18709fa8741f3af9d6207f81d5657b68e05e64f105a8" }, "gear": { "category": "objects", "moji": "⚙", + "description": "gear", "unicodeVersion": "4.1", "digest": "c5ba354c0f7a36dce95477091984e352ecc59af8c9f26a94ad8e296dc042b9de" }, "gem": { "category": "objects", "moji": "💎", + "description": "gem stone", "unicodeVersion": "6.0", "digest": "180e66f19d9285e02d0a5e859722c608206826e80323942b9938fc49d44973b1" }, "gemini": { "category": "symbols", "moji": "♊", + "description": "gemini", "unicodeVersion": "1.1", "digest": "278239c598d490a110f1f3f52fc3b85259be8e76034b38228ef3f68d7ddd8cdd" }, "ghost": { "category": "people", "moji": "👻", + "description": "ghost", "unicodeVersion": "6.0", "digest": "80d528fcf8ef9198631527547e43a608a4332a799f9e5550b8318dec67c9c4d2" }, "gift": { "category": "objects", "moji": "🎁", + "description": "wrapped present", "unicodeVersion": "6.0", "digest": "4061a84a59f0300473299678c43e533341eb965db09597fffc6e221fd7b77376" }, "gift_heart": { "category": "symbols", "moji": "💝", + "description": "heart with ribbon", "unicodeVersion": "6.0", "digest": "5420199b515b9b32c964a3c19d87e07461639e3068a939dae26c6436335c0cee" }, "girl": { "category": "people", "moji": "👧", + "description": "girl", "unicodeVersion": "6.0", "digest": "8d2d0b72a91e6e44921b71030ffc4c89c0f50f1364787784afe1e7e568cf1bc6" }, "girl_tone1": { "category": "people", "moji": "👧🏻", + "description": "girl tone 1", "unicodeVersion": "8.0", "digest": "bda12a6b38994a578ee65166bbdd93ea04df4101697b52ed236de8d687df09de" }, "girl_tone2": { "category": "people", "moji": "👧🏼", + "description": "girl tone 2", "unicodeVersion": "8.0", "digest": "de7a0925c30b7181a289f71b1a849c1b7751ee8c104e8f2029bd9c2fe3f91c64" }, "girl_tone3": { "category": "people", "moji": "👧🏽", + "description": "girl tone 3", "unicodeVersion": "8.0", "digest": "e41272816db0e642d003dce7cb262e1593a592251f46729f7830f4515149e1f2" }, "girl_tone4": { "category": "people", "moji": "👧🏾", + "description": "girl tone 4", "unicodeVersion": "8.0", "digest": "8d6a4513ecbf08408c0ecc5336767777a2216f7a19437faf9e51f65101822469" }, "girl_tone5": { "category": "people", "moji": "👧🏿", + "description": "girl tone 5", "unicodeVersion": "8.0", "digest": "f55e4b16a41b6f5e3c817a301420360ba4486e4e82e1092a56a3e3cc4069087d" }, "globe_with_meridians": { "category": "symbols", "moji": "🌐", + "description": "globe with meridians", "unicodeVersion": "6.0", "digest": "725bebeb3c09a9e3701ebe49e672dcfbf2b73575e05f0821263511577b013b75" }, "goal": { "category": "activity", "moji": "🥅", + "description": "goal net", "unicodeVersion": "9.0", "digest": "7088c432f276ff6f447dc0d431b9062b394fb401de1072fe59ca56267bfd6717" }, "goat": { "category": "nature", "moji": "🐐", + "description": "goat", "unicodeVersion": "6.0", "digest": "d07e384d08529ddcaddd2710f2ad913e5665dc15d5f99c28e16dadd245a111e8" }, "golf": { "category": "activity", "moji": "⛳", + "description": "flag in hole", "unicodeVersion": "5.2", "digest": "eed79364754eec97855e3c7b584f347ae139d9ddb4eb7fb66c00867610b8f1c1" }, "golfer": { "category": "activity", "moji": "🏌", + "description": "golfer", "unicodeVersion": "7.0", "digest": "7d7ecc6e226596f646030a4109c2b0001ef0cc690e4863e450bf5d29e7a90344" }, "gorilla": { "category": "nature", "moji": "🦍", + "description": "gorilla", "unicodeVersion": "9.0", "digest": "4a564dc14f8ae5450d094f6410ec7f099a7f07dc5254b6395f44a35527bdb4b7" }, "grapes": { "category": "food", "moji": "🍇", + "description": "grapes", "unicodeVersion": "6.0", "digest": "74d1a09ab411234a84d025a2e717e7ec5791bc02aad29853896d21c0f0283c50" }, "green_apple": { "category": "food", "moji": "🍏", + "description": "green apple", "unicodeVersion": "6.0", "digest": "457490e9b2b20894f50768262d63f1021717079da104d4847076b3fa779e9a21" }, "green_book": { "category": "objects", "moji": "📗", + "description": "green book", "unicodeVersion": "6.0", "digest": "370f635b200efe5e4a9f17da58bd22500e258e61d17795cef375f19c9a45468f" }, "green_heart": { "category": "symbols", "moji": "💚", + "description": "green heart", "unicodeVersion": "6.0", "digest": "f71e30416d9019873f2ed38ef375c48386424ff60b5a07b89b15dc9e0a3970f9" }, "grey_exclamation": { "category": "symbols", "moji": "❕", + "description": "white exclamation mark ornament", "unicodeVersion": "6.0", "digest": "2fa1d356e12c17cc4025e43afb6c3070385f677102a35223302fda46c47a9b03" }, "grey_question": { "category": "symbols", "moji": "❔", + "description": "white question mark ornament", "unicodeVersion": "6.0", "digest": "e1035bcbf0f66d238ef478ba451f5cf2c51627fbf101ed03bad3b2bf38db8aa2" }, "grimacing": { "category": "people", "moji": "😬", + "description": "grimacing face", "unicodeVersion": "6.1", "digest": "2cedad13b8b2a1d4385ca6fa88a251eb7757a4c65dd6d362267864a01247846b" }, "grin": { "category": "people", "moji": "😁", + "description": "grinning face with smiling eyes", "unicodeVersion": "6.0", "digest": "634b2f37e32e57ed6edc7f371993a92e34137dd21ba393de5227cfbbe2422815" }, "grinning": { "category": "people", "moji": "😀", + "description": "grinning face", "unicodeVersion": "6.1", "digest": "cef76aa41771db9fd1d6bd9b4233c22c1fb1931494af54cab29e6347ed9b678d" }, "guardsman": { "category": "people", "moji": "💂", + "description": "guardsman", "unicodeVersion": "6.0", "digest": "17bc7fad6b8c8dbd015bb709380d129f8b8e1e971062d15e6ab0b2e63e500564" }, "guardsman_tone1": { "category": "people", "moji": "💂🏻", + "description": "guardsman tone 1", "unicodeVersion": "8.0", "digest": "c531ecb101bdf9ce1db18e1567882e6db927410237100b0a2492a1401860246e" }, "guardsman_tone2": { "category": "people", "moji": "💂🏼", + "description": "guardsman tone 2", "unicodeVersion": "8.0", "digest": "602168c5204af0f1de8b4aa5863b192ef20c19d263999377aa5eb60f98311732" }, "guardsman_tone3": { "category": "people", "moji": "💂🏽", + "description": "guardsman tone 3", "unicodeVersion": "8.0", "digest": "d0a85de46dd02c7bd6cb14bff0f22d2db9083d4b171a8806c83363b49f3dd9ef" }, "guardsman_tone4": { "category": "people", "moji": "💂🏾", + "description": "guardsman tone 4", "unicodeVersion": "8.0", "digest": "1c9d4d72b6b50bdac8271613b6d2a38340ec2067bc344e8ee2a3c863fd5c23a1" }, "guardsman_tone5": { "category": "people", "moji": "💂🏿", + "description": "guardsman tone 5", "unicodeVersion": "8.0", "digest": "9899a796d01842e495d716fbe737a16d85724f7d3e23f50807ec2bc70f057318" }, "guitar": { "category": "activity", "moji": "🎸", + "description": "guitar", "unicodeVersion": "6.0", "digest": "a1027ceae4dd3ea270740587c9d373329e5677e375c9e00af6ae3275e0b67500" }, "gun": { "category": "objects", "moji": "🔫", + "description": "pistol", "unicodeVersion": "6.0", "digest": "fc12b577df2283e7b336f23774f9cfe5b79f1d26ddd28a64a560519b28d94ca5" }, "haircut": { "category": "people", "moji": "💇", + "description": "haircut", "unicodeVersion": "6.0", "digest": "b243a04f5ca889accd45e7abe095ac5caa92274ed95103f5966a36b415fff412" }, "haircut_tone1": { "category": "people", "moji": "💇🏻", + "description": "haircut tone 1", "unicodeVersion": "8.0", "digest": "a58d0cff1427b80dfd7a9ea5267b4a181e9faaac6a51a0165db522f668b4cf91" }, "haircut_tone2": { "category": "people", "moji": "💇🏼", + "description": "haircut tone 2", "unicodeVersion": "8.0", "digest": "675083ff40001405f8de99268477d50dd8594ff6ca40ddfd442dd42ad76e8216" }, "haircut_tone3": { "category": "people", "moji": "💇🏽", + "description": "haircut tone 3", "unicodeVersion": "8.0", "digest": "70d7581e49c315a3771dd61a3713229886db32aaaeb3af078a69cc042f809150" }, "haircut_tone4": { "category": "people", "moji": "💇🏾", + "description": "haircut tone 4", "unicodeVersion": "8.0", "digest": "ec5e3e909eb3bc375ef9cc0fe0e0f90b33f44f273ada91ccf415bbc43b8ffbfc" }, "haircut_tone5": { "category": "people", "moji": "💇🏿", + "description": "haircut tone 5", "unicodeVersion": "8.0", "digest": "7c89739ee458546a808fded7f96d9354c47a76883ebb262d5f5abeafd021260e" }, "hamburger": { "category": "food", "moji": "🍔", + "description": "hamburger", "unicodeVersion": "6.0", "digest": "48204235238bd89d3a69f319f65135102f3d6b181eec241d4d86b302bbffa9bf" }, "hammer": { "category": "objects", "moji": "🔨", + "description": "hammer", "unicodeVersion": "6.0", "digest": "d0e7830539d935fcd82820c4e0c1d724f0756dfc83a51171fe0f4b36b69fac42" }, "hammer_pick": { "category": "objects", "moji": "⚒", + "description": "hammer and pick", "unicodeVersion": "4.1", "digest": "aa0445f43bca58d17afa7f3577632ca7775f5a28336385b3020b268b15b18142" }, "hamster": { "category": "nature", "moji": "🐹", + "description": "hamster face", "unicodeVersion": "6.0", "digest": "a7e7582e8b1bccd5b7df27ccb05e353a3f0e39bdeb40877732706b9d74a70de1" }, "hand_splayed": { "category": "people", "moji": "🖐", + "description": "raised hand with fingers splayed", "unicodeVersion": "7.0", "digest": "c51a30cb7e575d29ffed16780a6c95ae3f300b8ac523012f4a6e116d68c1fd15" }, "hand_splayed_tone1": { "category": "people", "moji": "🖐🏻", + "description": "raised hand with fingers splayed tone 1", "unicodeVersion": "8.0", "digest": "c31fb44a982ed8808e1c311ec1b0b9c5afcb47f16bb1fc731dc483adf8f0d049" }, "hand_splayed_tone2": { "category": "people", "moji": "🖐🏼", + "description": "raised hand with fingers splayed tone 2", "unicodeVersion": "8.0", "digest": "56a236881184e9ffad54613fa08a67368c432af738f5254fb1cd87b20368acdf" }, "hand_splayed_tone3": { "category": "people", "moji": "🖐🏽", + "description": "raised hand with fingers splayed tone 3", "unicodeVersion": "8.0", "digest": "9242ca97dfd2bbc1947228f6535029afb31f8feb72c14ff4b7f2deea30217425" }, "hand_splayed_tone4": { "category": "people", "moji": "🖐🏾", + "description": "raised hand with fingers splayed tone 4", "unicodeVersion": "8.0", "digest": "43348d9fd3d43b3c45cebaf663bf181bcad3b6df841a5aeed838180db2cdd481" }, "hand_splayed_tone5": { "category": "people", "moji": "🖐🏿", + "description": "raised hand with fingers splayed tone 5", "unicodeVersion": "8.0", "digest": "4b3a0aba7829772fec09f26d6facc19a2f822d2998015297b18b5cab85190ee2" }, "handbag": { "category": "people", "moji": "👜", + "description": "handbag", "unicodeVersion": "6.0", "digest": "45410a3eed0c2e3f68748d7649fa9e33a90f4e80d5291206bdd0b40380c6da45" }, "handball": { "category": "activity", "moji": "🤾", + "description": "handball", "unicodeVersion": "9.0", "digest": "94ceb28024eb3259d8b137cafd7438773e717fbc04f5da810f85e43ca0fa9e00" }, "handball_tone1": { "category": "activity", "moji": "🤾🏻", + "description": "handball tone 1", "unicodeVersion": "9.0", "digest": "8bec4de0d05c80e335e44d65598d186ca92696977353c9fd9c2a5efa122cb842" }, "handball_tone2": { "category": "activity", "moji": "🤾🏼", + "description": "handball tone 2", "unicodeVersion": "9.0", "digest": "2ff4131e1e2f089b315d8e176c9348877c26c2bd03706fb75d41bc61bc99bf93" }, "handball_tone3": { "category": "activity", "moji": "🤾🏽", + "description": "handball tone 3", "unicodeVersion": "9.0", "digest": "224a71f94dd37d3729325d11412334667a81422e21f6d7c008730ff350f51a80" }, "handball_tone4": { "category": "activity", "moji": "🤾🏾", + "description": "handball tone 4", "unicodeVersion": "9.0", "digest": "a5f7a9db790565981bad2d0d9e09554c8c509a8179b4705a418300d58a7894b4" }, "handball_tone5": { "category": "activity", "moji": "🤾🏿", + "description": "handball tone 5", "unicodeVersion": "9.0", "digest": "00404572d4683f2e8e8a494aa733e96fbec1723634d0a8cb8d75f2829a789d27" }, "handshake": { "category": "people", "moji": "🤝", + "description": "handshake", "unicodeVersion": "9.0", "digest": "cb4b08b70560908f96bda0aecd2f4c966bea180f9b7200e4c81d342dc8d36087" }, "handshake_tone1": { "category": "people", "moji": "🤝🏻", + "description": "handshake tone 1", "unicodeVersion": "9.0", "digest": "40470e224683ba375ed8698c0cbd560556be5a8898237ddf504377a3a7e89ff0" }, "handshake_tone2": { "category": "people", "moji": "🤝🏼", + "description": "handshake tone 2", "unicodeVersion": "9.0", "digest": "77ed378243bf682f1f4f1a8caeabcbedf772f54631cc40ea46c099e46a499b18" }, "handshake_tone3": { "category": "people", "moji": "🤝🏽", + "description": "handshake tone 3", "unicodeVersion": "9.0", "digest": "81b95050f0878b617f5d2640e34031c26a0072e46ca5a688eb4356e48bc74c92" }, "handshake_tone4": { "category": "people", "moji": "🤝🏾", + "description": "handshake tone 4", "unicodeVersion": "9.0", "digest": "74919a6f026fbbd0ccdbdbd4288d1b2ef3bda8930e9142c07736db4a7f3ef345" }, "handshake_tone5": { "category": "people", "moji": "🤝🏿", + "description": "handshake tone 5", "unicodeVersion": "9.0", "digest": "a30d662bfad0074ca7e32cf6f7229b643b636c4beaec496777eb7e1d5b6fc470" }, "hash": { "category": "symbols", "moji": "#⃣", + "description": "number sign", "unicodeVersion": "3.0", "digest": "01c8b577953010bff0c20f797c2c96ab5d98d4e6ac179c4895a78f34ea904655" }, "hatched_chick": { "category": "nature", "moji": "🐥", + "description": "front-facing baby chick", "unicodeVersion": "6.0", "digest": "006571b9e9e839ec9fcb1a911b935c8ca71eb8bcdce9775bee6a2a4c7c927277" }, "hatching_chick": { "category": "nature", "moji": "🐣", + "description": "hatching chick", "unicodeVersion": "6.0", "digest": "fd7f69fa186407f80de59dec5116e318325a5743ee0e8bba1db541f1e57e7f74" }, "head_bandage": { "category": "people", "moji": "🤕", + "description": "face with head-bandage", "unicodeVersion": "8.0", "digest": "d09019a73e203b38cc43729a96163147de88e09eab8adb073888e55366854c72" }, "headphones": { "category": "activity", "moji": "🎧", + "description": "headphone", "unicodeVersion": "6.0", "digest": "34f9d5598158d5d6f978a5ea5c5aa9948bb2990625565a3afad7710f864fbe2f" }, "hear_no_evil": { "category": "nature", "moji": "🙉", + "description": "hear-no-evil monkey", "unicodeVersion": "6.0", "digest": "53b030b6d6f4ed1a734fa7d48b46f42eb1b2b01653202c1838b742082f08c4bf" }, "heart": { "category": "symbols", "moji": "❤", + "description": "heavy black heart", "unicodeVersion": "1.1", "digest": "92be652ec3e50c6e7393440b5d52b88a367f98a28dffe12660095ed3253aa6c0" }, "heart_decoration": { "category": "symbols", "moji": "💟", + "description": "heart decoration", "unicodeVersion": "6.0", "digest": "6ec5bbf3aa75c6f43eb3dc05e9204366936e8b6b4219310bacdc2fc45f51e245" }, "heart_exclamation": { "category": "symbols", "moji": "❣", + "description": "heavy heart exclamation mark ornament", "unicodeVersion": "1.1", "digest": "5985ea4d82232a2a07052a59db268aed9ac943895d0c82f637595bb5386329a6" }, "heart_eyes": { "category": "people", "moji": "😍", + "description": "smiling face with heart-shaped eyes", "unicodeVersion": "6.0", "digest": "0eff616517a6252ec89d47d9b4ad85589bcf2bdc7f490578934350acb84b2fcc" }, "heart_eyes_cat": { "category": "people", "moji": "😻", + "description": "smiling cat face with heart-shaped eyes", "unicodeVersion": "6.0", "digest": "8a1f28b97d661ca4cff5ee13889ca61b5fa745ccb590e80832b7d7701df101d6" }, "heartbeat": { "category": "symbols", "moji": "💓", + "description": "beating heart", "unicodeVersion": "6.0", "digest": "c9ec024943439d476df6f5ec3a6b30508365a7af3427671a80de3ef2f4f95ffe" }, "heartpulse": { "category": "symbols", "moji": "💗", + "description": "growing heart", "unicodeVersion": "6.0", "digest": "281d8aebfea37db5b7fe82d9115be167006881fe29ab64a5b09ac92ac27a2309" }, "hearts": { "category": "symbols", "moji": "♥", + "description": "black heart suit", "unicodeVersion": "1.1", "digest": "271429d12c40be921897005b7bdd08f9518960af1e1e6f56bb0060f1f183651e" }, "heavy_check_mark": { "category": "symbols", "moji": "✔", + "description": "heavy check mark", "unicodeVersion": "1.1", "digest": "e347728e1290eb9e7b0742d628e2fd124fc049e0774f8a6ddf8e5286e7318718" }, "heavy_division_sign": { "category": "symbols", "moji": "➗", + "description": "heavy division sign", "unicodeVersion": "6.0", "digest": "c1e8c40f0788f140b1c5fcb81ed9b5ce1bcfa5988bb8140ed2808e9cb7e0d651" }, "heavy_dollar_sign": { "category": "symbols", "moji": "💲", + "description": "heavy dollar sign", "unicodeVersion": "6.0", "digest": "7cdeef38348654b93d566e01a48973281cb404a63d0b75b3bad51032887f3f55" }, "heavy_minus_sign": { "category": "symbols", "moji": "➖", + "description": "heavy minus sign", "unicodeVersion": "6.0", "digest": "e5335cc6b22abdce49a6127c34269b65a4a6643ddd3253d9baac425089143e7d" }, "heavy_multiplication_x": { "category": "symbols", "moji": "✖", + "description": "heavy multiplication x", "unicodeVersion": "1.1", "digest": "64bbe9e9716a922e405d2f6d3b6d803863a53fac80ff8cd775899971046cb1ca" }, "heavy_plus_sign": { "category": "symbols", "moji": "➕", + "description": "heavy plus sign", "unicodeVersion": "6.0", "digest": "d0d8ade2020ceb252205180b85c66e665856e6cb505518d395b9913b0b24b746" }, "helicopter": { "category": "travel", "moji": "🚁", + "description": "helicopter", "unicodeVersion": "6.0", "digest": "4bd6fd13650fbe3a19cfffeffe6c21b1cda74bd6af64c5dc5999185e35444bc3" }, "helmet_with_cross": { "category": "people", "moji": "⛑", + "description": "helmet with white cross", "unicodeVersion": "5.2", "digest": "8286107391d44b9cd7fce5dc83bfdebbcdcf5a8214c46a8990732ec40263ed77" }, "herb": { "category": "nature", "moji": "🌿", + "description": "herb", "unicodeVersion": "6.0", "digest": "9fe8ed65515ede59d0926dcf98f14e2498785e1965610aa0dd56eca9b4bedad9" }, "hibiscus": { "category": "nature", "moji": "🌺", + "description": "hibiscus", "unicodeVersion": "6.0", "digest": "c442e8eacbd8727bd154bd39692a9a2a03ea2f674b9670ad8361f78a038afe49" }, "high_brightness": { "category": "symbols", "moji": "🔆", + "description": "high brightness symbol", "unicodeVersion": "6.0", "digest": "35ced42426dcfd5214c2c6c577dce84bb708156433945e6b6adaff7ea530cc57" }, "high_heel": { "category": "people", "moji": "👠", + "description": "high-heeled shoe", "unicodeVersion": "6.0", "digest": "1e7c7aba50eb1d02cf1d9aa372caca741a6005cf47f68dfa75b7310c3cb18f05" }, "hockey": { "category": "activity", "moji": "🏒", + "description": "ice hockey stick and puck", "unicodeVersion": "8.0", "digest": "2d00fb17baa617e799db8e9b1771cc365bb4545c7633df0123e66e1a6e2ed25d" }, "hole": { "category": "objects", "moji": "🕳", + "description": "hole", "unicodeVersion": "7.0", "digest": "8b5539f6f24f09d5d68ffd56be5aa2a8a2f753a8dfbf64892fb02c8f2703e920" }, "homes": { "category": "travel", "moji": "🏘", + "description": "house buildings", "unicodeVersion": "7.0", "digest": "cd512f2b4ce747325607d47da48e083dbfe38a44b85b2522bc372bd105afd25f" }, "honey_pot": { "category": "food", "moji": "🍯", + "description": "honey pot", "unicodeVersion": "6.0", "digest": "f6eec8c32fbd1b461446dc6c5d5031c43e6ee9685dc9b1ea1b839114e48c4eee" }, "horse": { "category": "nature", "moji": "🐴", + "description": "horse face", "unicodeVersion": "6.0", "digest": "e377649a9549835770a2a721a92570f699255f88efa646029638eb8ec5f10e3d" }, "horse_racing": { "category": "activity", "moji": "🏇", + "description": "horse racing", "unicodeVersion": "6.0", "digest": "3b98e94e9c028ad85b9a750cc61db5ee3ac23cf5ad9243ea3e996b1f772bad54" }, "horse_racing_tone1": { "category": "activity", "moji": "🏇🏻", + "description": "horse racing tone 1", "unicodeVersion": "8.0", "digest": "382d8e4502ed34fc1bbf1779ce483bc2e22b83f89c91746c11a5d7aea656d446" }, "horse_racing_tone2": { "category": "activity", "moji": "🏇🏼", + "description": "horse racing tone 2", "unicodeVersion": "8.0", "digest": "198df9973b492ea63e5cfc210dd9591750ccce04a6380adc1dc5b4cb0462a8cd" }, "horse_racing_tone3": { "category": "activity", "moji": "🏇🏽", + "description": "horse racing tone 3", "unicodeVersion": "8.0", "digest": "a67f95fc92c366750ebad3c4db92982893d67a5ed78163c8cc809ac40d2ab9a3" }, "horse_racing_tone4": { "category": "activity", "moji": "🏇🏾", + "description": "horse racing tone 4", "unicodeVersion": "8.0", "digest": "986b1706c4a3395b58a8ae3b7609ffdd4424dfefcbf26c88c8085f4f6379734e" }, "horse_racing_tone5": { "category": "activity", "moji": "🏇🏿", + "description": "horse racing tone 5", "unicodeVersion": "8.0", "digest": "66656b5e3d0f43f16f983f9db6214b07aac73b143eeff6475782f98aa5b9ba53" }, "hospital": { "category": "travel", "moji": "🏥", + "description": "hospital", "unicodeVersion": "6.0", "digest": "034573e76df444f5b0eb7aff3a4103e4b49a1813869155ab3ae29a6fc0c6c8a2" }, "hot_pepper": { "category": "food", "moji": "🌶", + "description": "hot pepper", "unicodeVersion": "7.0", "digest": "0b05777d42698196a10db17d04030175b1dfa772d06288f71d666d5f8d3fddbc" }, "hotdog": { "category": "food", "moji": "🌭", + "description": "hot dog", "unicodeVersion": "8.0", "digest": "7a25bbd1a7531fd34a22c654c0931d9e74bea2bbe7baa9f9cbd88f43baa79fb5" }, "hotel": { "category": "travel", "moji": "🏨", + "description": "hotel", "unicodeVersion": "6.0", "digest": "2d78e0ad4cfb0caad778c7de49fefd6e8356afe902a43e3f1c40bceb6b0be422" }, "hotsprings": { "category": "symbols", "moji": "♨", + "description": "hot springs", "unicodeVersion": "1.1", "digest": "4c10c3a974b44693e8cbe91365c8b8d7f14f62db234cc516b6e54c08a6bacaed" }, "hourglass": { "category": "objects", "moji": "⌛", + "description": "hourglass", "unicodeVersion": "1.1", "digest": "f0bae8392aaf6f75a83f5d8914936b8650665b24ba1b232fa546b71545dd9acd" }, "hourglass_flowing_sand": { "category": "objects", "moji": "⏳", + "description": "hourglass with flowing sand", "unicodeVersion": "6.0", "digest": "2d077729f40fc04007a933e97356bd511cbd8be76b8c55962ca3fa0d8b828e23" }, "house": { "category": "travel", "moji": "🏠", + "description": "house building", "unicodeVersion": "6.0", "digest": "b4ac25979fbe161ada0d2a75769aa7552d2371d37d78cddba4ffdc7f076d3279" }, "house_abandoned": { "category": "travel", "moji": "🏚", + "description": "derelict house building", "unicodeVersion": "7.0", "digest": "6e1a58533fbfe88a0eb03668c9f17c5c654a6cc7734ed798d4a885400f823610" }, "house_with_garden": { "category": "travel", "moji": "🏡", + "description": "house with garden", "unicodeVersion": "6.0", "digest": "817463f23ec0a849393ba75c333e822b4d253cd4db998c127e90d1b924f35d20" }, "hugging": { "category": "people", "moji": "🤗", + "description": "hugging face", "unicodeVersion": "8.0", "digest": "69810a98b1247e1f1e496aa757e428189ef5cc086764fabd8189cf1eef82234f" }, "hushed": { "category": "people", "moji": "😯", + "description": "hushed face", "unicodeVersion": "6.1", "digest": "22586107f7399eff64538a52929dade152633aa268fc5ec4e6fe1c0e00a7bd89" }, "ice_cream": { "category": "food", "moji": "🍨", + "description": "ice cream", "unicodeVersion": "6.0", "digest": "d1a8e685f2ecf83dead28733859e369d6ce120a2669cdab97dc4423547d472ac" }, "ice_skate": { "category": "activity", "moji": "⛸", + "description": "ice skate", "unicodeVersion": "5.2", "digest": "41ef65c143bc068868fa64080ffd447d91aa3fe2a39e69ecaa97022820af4dcd" }, "icecream": { "category": "food", "moji": "🍦", + "description": "soft ice cream", "unicodeVersion": "6.0", "digest": "22cfe17b80cbd2a0377ee90da45bd40d33533c914b2639d363fbb1f00714e194" }, "id": { "category": "symbols", "moji": "🆔", + "description": "squared id", "unicodeVersion": "6.0", "digest": "bcf0922e083821d3be7951893084ea0d72a0110ef0b20d11dfec24dd70633893" }, "ideograph_advantage": { "category": "symbols", "moji": "🉐", + "description": "circled ideograph advantage", "unicodeVersion": "6.0", "digest": "0b6bf59f63fda1afa92d652814a778a056c3f4abdd9cf3f6796068bd71783051" }, "imp": { "category": "people", "moji": "👿", + "description": "imp", "unicodeVersion": "6.0", "digest": "52598cf2441988f875ccb4e479637baefc679e3ca64e9a6400e56488b0fde811" }, "inbox_tray": { "category": "objects", "moji": "📥", + "description": "inbox tray", "unicodeVersion": "6.0", "digest": "d5d9497022b5318fcfbfdfcd56df9c65dd8f4a4cb5e6283ca260836df57da301" }, "incoming_envelope": { "category": "objects", "moji": "📨", + "description": "incoming envelope", "unicodeVersion": "6.0", "digest": "310b7bdcca93452fe10c72c03d0aafa12b98e5d3408896d275d06d3693812c7a" }, "information_desk_person": { "category": "people", "moji": "💁", + "description": "information desk person", "unicodeVersion": "6.0", "digest": "9f12a4a58a650e8e1d3836ef857003c3ccd42ad4203a2479eb95100bf6559064" }, "information_desk_person_tone1": { "category": "people", "moji": "💁🏻", + "description": "information desk person tone 1", "unicodeVersion": "8.0", "digest": "6674f2e059eff7cfd7fd6abc800da37c4f1087feb4ff26c9e4e31aa29fdf9921" }, "information_desk_person_tone2": { "category": "people", "moji": "💁🏼", + "description": "information desk person tone 2", "unicodeVersion": "8.0", "digest": "9983412ecd130b7e9cfb078167016c06fd043b6f9f3c26d21733ca3f059fd109" }, "information_desk_person_tone3": { "category": "people", "moji": "💁🏽", + "description": "information desk person tone 3", "unicodeVersion": "8.0", "digest": "d8907bf47af5722127afca8fc0da587eab33044a6c60a94890983deb8d6f7a66" }, "information_desk_person_tone4": { "category": "people", "moji": "💁🏾", + "description": "information desk person tone 4", "unicodeVersion": "8.0", "digest": "3be086d4edfe9ca8e4a364b4e8d09b81b5b594b5eeb9ffdf6370179fb3118658" }, "information_desk_person_tone5": { "category": "people", "moji": "💁🏿", + "description": "information desk person tone 5", "unicodeVersion": "8.0", "digest": "2fde4e98dd11c5c29c89cad7cbb7bd2d5077dfad07913b20e01955b2d0dfad40" }, "information_source": { "category": "symbols", "moji": "ℹ", + "description": "information source", "unicodeVersion": "3.0", "digest": "b6bf3cce86d42c2e3c46470baab4af01e900b8ae337b605c3da07c3eba671269" }, "innocent": { "category": "people", "moji": "😇", + "description": "smiling face with halo", "unicodeVersion": "6.0", "digest": "20f8d856bc3e46f4b1173cea05d4577e1c61f06b2daba46e57db90f4066bb428" }, "interrobang": { "category": "symbols", "moji": "⁉", + "description": "exclamation question mark", "unicodeVersion": "3.0", "digest": "92a2d5b4c0bd6714e402f6f12fe19774cb41d081b5e9c23c415ce794224d8117" }, "iphone": { "category": "objects", "moji": "📱", + "description": "mobile phone", "unicodeVersion": "6.0", "digest": "1ebc54215713cd4bf1c1e50770999f2512bb4fea29e37d0bb3a8aa2460ff875d" }, "island": { "category": "travel", "moji": "🏝", + "description": "desert island", "unicodeVersion": "7.0", "digest": "7f9eb5c0cd865762f7a0f187e09c1be442de7010e7c2e113d56aae998597c90d" }, "izakaya_lantern": { "category": "objects", "moji": "🏮", + "description": "izakaya lantern", "unicodeVersion": "6.0", "digest": "fbdc290e666d43d0776a73b955c26df4518692b35e72742e073705fc4ca2ae88" }, "jack_o_lantern": { "category": "nature", "moji": "🎃", + "description": "jack-o-lantern", "unicodeVersion": "6.0", "digest": "78d666c2e80f64bfb6796f53e5ba4960a83ec36192110e8661031bee2b5e370a" }, "japan": { "category": "travel", "moji": "🗾", + "description": "silhouette of japan", "unicodeVersion": "6.0", "digest": "e7d9d6ebf9047fdd3c52e074ba259659c6d8e51a6abae3cdb8d6cf6dbf9a93fe" }, "japanese_castle": { "category": "travel", "moji": "🏯", + "description": "japanese castle", "unicodeVersion": "6.0", "digest": "938ae132c403330288223b88d28c19a47224d4f254fbc2366ecef73d9633112c" }, "japanese_goblin": { "category": "people", "moji": "👺", + "description": "japanese goblin", "unicodeVersion": "6.0", "digest": "63d4bcf58b9d0c29612994432aad2ae35819fdd2890674e60a2f1d51601b742e" }, "japanese_ogre": { "category": "people", "moji": "👹", + "description": "japanese ogre", "unicodeVersion": "6.0", "digest": "434ceedd102e7dcbc07e086811673dd63659ddf8c3ec4d029a3d759a0abfcbdb" }, "jeans": { "category": "people", "moji": "👖", + "description": "jeans", "unicodeVersion": "6.0", "digest": "f986ad32e419cca81c995f8371f0189d1490172a97ebbeac60054a1af08949c5" }, "joy": { "category": "people", "moji": "😂", + "description": "face with tears of joy", "unicodeVersion": "6.0", "digest": "75d7a05043523d290c46d3b313b19ed3c95271f1110bcf234cf13d4273625b08" }, "joy_cat": { "category": "people", "moji": "😹", + "description": "cat face with tears of joy", "unicodeVersion": "6.0", "digest": "a65c999604147e5e20170fcb14f80a1ff0a633f991492e1f790b2ad4caec7b7e" }, "joystick": { "category": "objects", "moji": "🕹", + "description": "joystick", "unicodeVersion": "7.0", "digest": "671ee588f397a96f27056a67e6a06d6e8d22c2109ec57b2859badb5fec9cf8dd" }, "juggling": { "category": "activity", "moji": "🤹", + "description": "juggling", "unicodeVersion": "9.0", "digest": "1f5dafa78de8b37f3df88fdf3084d2380666bd74ab2f449754d8724f6f8dbfa5" }, "juggling_tone1": { "category": "activity", "moji": "🤹🏻", + "description": "juggling tone 1", "unicodeVersion": "9.0", "digest": "b0b4d020148c896be69c28b08e3c486f6db270d138c7ccf4be362b29eb99878d" }, "juggling_tone2": { "category": "activity", "moji": "🤹🏼", + "description": "juggling tone 2", "unicodeVersion": "9.0", "digest": "cfe0c1649b2fdca03673e0e64f3a7d06d4bd49b8954c769aeb7eb88b70ec99f4" }, "juggling_tone3": { "category": "activity", "moji": "🤹🏽", + "description": "juggling tone 3", "unicodeVersion": "9.0", "digest": "7f87022722008bb265abe245e8157dc7a61944f5da62b3cf86f26ee1b3bdef63" }, "juggling_tone4": { "category": "activity", "moji": "🤹🏾", + "description": "juggling tone 4", "unicodeVersion": "9.0", "digest": "1f00da8c05582c95501cc6c3fe5ce0f9bfbc16789dcee59844a8fe7831198583" }, "juggling_tone5": { "category": "activity", "moji": "🤹🏿", + "description": "juggling tone 5", "unicodeVersion": "9.0", "digest": "a195bf734788eb7961c00dbc05255a49da8b9d5042fada29b26cc20393d3ce52" }, "kaaba": { "category": "travel", "moji": "🕋", + "description": "kaaba", "unicodeVersion": "8.0", "digest": "a4618782f9583f077bd383965f1c91b9985a949bb7b6cec7af22914e7f5e9ab6" }, "key": { "category": "objects", "moji": "🔑", + "description": "key", "unicodeVersion": "6.0", "digest": "66719fa77a50a0827c8d47237e2704c03e38186e6fef80627a765473b2294c2e" }, "key2": { "category": "objects", "moji": "🗝", + "description": "old key", "unicodeVersion": "7.0", "digest": "f57240a014a9da5da3d4d98c17d0a55e0ff2e5f2d22731d2fc867105cff54c6e" }, "keyboard": { "category": "objects", "moji": "⌨", + "description": "keyboard", "unicodeVersion": "1.1", "digest": "34da8ff62ca964142f9281b80123dbba74deaac8d77fa61758c30cfb36c31386" }, "kimono": { "category": "people", "moji": "👘", + "description": "kimono", "unicodeVersion": "6.0", "digest": "637182590e256c8fb74ce4c0565f5180c07f06e3bdebf30138ed3259b209c27f" }, "kiss": { "category": "people", "moji": "💋", + "description": "kiss mark", "unicodeVersion": "6.0", "digest": "62f9b9ffcb01558cd5bb829344a1d1d399511663ff5235405c1f786c9416a94d" }, "kiss_mm": { "category": "people", "moji": "👨‍❤️‍💋‍👨", + "description": "kiss (man,man)", "unicodeVersion": "6.0", "digest": "6b0ae32ecb7ec0f0f43dc7a1350711185cce114c52752395f364ddbfb4f1fff4" }, "kiss_ww": { "category": "people", "moji": "👩‍❤️‍💋‍👩", + "description": "kiss (woman,woman)", "unicodeVersion": "6.0", "digest": "6de420cf752e706b1b7e9522b1b9be62eda069cb028c8fd587caf39f6a142e6a" }, "kissing": { "category": "people", "moji": "😗", + "description": "kissing face", "unicodeVersion": "6.1", "digest": "b4a505f9e3d7fbd0ac60111f0e678cf425a5fd1abc65a3e9db59ae4abcfb8e85" }, "kissing_cat": { "category": "people", "moji": "😽", + "description": "kissing cat face with closed eyes", "unicodeVersion": "6.0", "digest": "a00431bf10601db4998e78433279167e52cbd36aed885399482529d5cdab8636" }, "kissing_closed_eyes": { "category": "people", "moji": "😚", + "description": "kissing face with closed eyes", "unicodeVersion": "6.0", "digest": "ae474db7daf80fe0b82ae1f2a11672cfcd9f9126e100f6e6d4b8a0d135dce39d" }, "kissing_heart": { "category": "people", "moji": "😘", + "description": "face throwing a kiss", "unicodeVersion": "6.0", "digest": "bce372573bd3b347b555c1cd22087e03e650df73c8e0284ab668bf6633251632" }, "kissing_smiling_eyes": { "category": "people", "moji": "😙", + "description": "kissing face with smiling eyes", "unicodeVersion": "6.1", "digest": "f0f8636cb1a02b93cc72ce1b194b890fca823d91e35926b889be3ecfae79207f" }, "kiwi": { "category": "food", "moji": "🥝", + "description": "kiwifruit", "unicodeVersion": "9.0", "digest": "70a3a05f333d9455d2da12eed970bc3baae416286848fed8e5dd31b5be0819be" }, "knife": { "category": "objects", "moji": "🔪", + "description": "hocho", "unicodeVersion": "6.0", "digest": "e6189e4843c6e80875b4952fcddb0c858f7c6039b9214bbec6a261a1358425df" }, "koala": { "category": "nature", "moji": "🐨", + "description": "koala", "unicodeVersion": "6.0", "digest": "c58f7e0abae42c2218a85efed0e04151df67187815bebca7f3db6f435e0dab4d" }, "koko": { "category": "symbols", "moji": "🈁", + "description": "squared katakana koko", "unicodeVersion": "6.0", "digest": "5f45eb49bbf298e1fadedfe6cccc297850fcaaa4535e4cc911d48d979af55807" }, "label": { "category": "objects", "moji": "🏷", + "description": "label", "unicodeVersion": "7.0", "digest": "9550ed50cedbc56eb1bd22a8a0809d837048a33d6e2e6e7d65c50d95fa05a85d" }, "large_blue_circle": { "category": "symbols", "moji": "🔵", + "description": "large blue circle", "unicodeVersion": "6.0", "digest": "0df3fb3b09a6269459a3d9a1fe78db572190a948680844cfe758f53b6a482ff4" }, "large_blue_diamond": { "category": "symbols", "moji": "🔷", + "description": "large blue diamond", "unicodeVersion": "6.0", "digest": "7f646b4e9de2788ed09e45f72cb512c269dda4989029b39bf9a2556659321651" }, "large_orange_diamond": { "category": "symbols", "moji": "🔶", + "description": "large orange diamond", "unicodeVersion": "6.0", "digest": "80ae005ef9d79190c777f00de0993f8b3cb783f7051d76e971640c8c0827c338" }, "last_quarter_moon": { "category": "nature", "moji": "🌗", + "description": "last quarter moon symbol", "unicodeVersion": "6.0", "digest": "3d1f276607c685d50f4b70d00a57750a57ad9ad84256dafd2dc8eef8c72300c3" }, "last_quarter_moon_with_face": { "category": "nature", "moji": "🌜", + "description": "last quarter moon with face", "unicodeVersion": "6.0", "digest": "d516825ba52dc67f5a01433fb9df2aa77742d38efde4225983ebc4882cbdfe5d" }, "laughing": { "category": "people", "moji": "😆", + "description": "smiling face with open mouth and tightly-closed ey", "unicodeVersion": "6.0", "digest": "e9ea994b39650740c4961f070ed492d86b3acf6e6a830a6dadaa3a6872e81b81" }, "leaves": { "category": "nature", "moji": "🍃", + "description": "leaf fluttering in wind", "unicodeVersion": "6.0", "digest": "56a7a0e767a6f214d340d1b5989efd99fec52c6aa306ec5c3328e32234a1631b" }, "ledger": { "category": "objects", "moji": "📒", + "description": "ledger", "unicodeVersion": "6.0", "digest": "e58cb714353e96a2891a5d97910ff79660e637af909b81c49c919d3735db55b4" }, "left_facing_fist": { "category": "people", "moji": "🤛", + "description": "left-facing fist", "unicodeVersion": "9.0", "digest": "7861be485beefae0de341df2f21576666e22f63511a033e785752f30c07291da" }, "left_facing_fist_tone1": { "category": "people", "moji": "🤛🏻", + "description": "left facing fist tone 1", "unicodeVersion": "9.0", "digest": "2e4c4dd96b0e4b46fe0f9ce5666344d266d0f17a8544cbae73d96638d1955296" }, "left_facing_fist_tone2": { "category": "people", "moji": "🤛🏼", + "description": "left facing fist tone 2", "unicodeVersion": "9.0", "digest": "b96a63a801175ce98a75f0edad7b5574251a3fbbd894d8ab3f21aeeda366cc13" }, "left_facing_fist_tone3": { "category": "people", "moji": "🤛🏽", + "description": "left facing fist tone 3", "unicodeVersion": "9.0", "digest": "99df84635513c2ebfef24df1bd3705233e02149eef788c7b82ca0548df6f6ea5" }, "left_facing_fist_tone4": { "category": "people", "moji": "🤛🏾", + "description": "left facing fist tone 4", "unicodeVersion": "9.0", "digest": "68954842ca725aec0aa39bce4aa81aad17ac30f5f298561dfa411feb07414cd3" }, "left_facing_fist_tone5": { "category": "people", "moji": "🤛🏿", + "description": "left facing fist tone 5", "unicodeVersion": "9.0", "digest": "a419b33fae82612dc860ff48950c0547a1642d4f0c94b6547324440837d3bb21" }, "left_luggage": { "category": "symbols", "moji": "🛅", + "description": "left luggage", "unicodeVersion": "6.0", "digest": "6625077767a51163ea20cbc299f3c13fd5ccf1b5ce365ee702ef1fef6be3dadf" }, "left_right_arrow": { "category": "symbols", "moji": "↔", + "description": "left right arrow", "unicodeVersion": "1.1", "digest": "560fcf1b794eb0d5269c73b3f8da57540cbb8a6f1a9af7a9d10b202252247e34" }, "leftwards_arrow_with_hook": { "category": "symbols", "moji": "↩", + "description": "leftwards arrow with hook", "unicodeVersion": "1.1", "digest": "504714c5559b1bd35aa469be83069a923d1a25f364cac08c10df0195749e7b26" }, "lemon": { "category": "food", "moji": "🍋", + "description": "lemon", "unicodeVersion": "6.0", "digest": "ccca25bb6ac47770dba3aaf75144128f9a73299061969b25a35ad1733dcde5fe" }, "leo": { "category": "symbols", "moji": "♌", + "description": "leo", "unicodeVersion": "1.1", "digest": "f2ed930e279699962f189e0cac519cc29d339b3e82debfdc90c5b0935a7543bb" }, "leopard": { "category": "nature", "moji": "🐆", + "description": "leopard", "unicodeVersion": "6.0", "digest": "d4a8964b6f2cdf6ddf074d0f1f2f65783a1a43eb4af426905fad0e60899939c7" }, "level_slider": { "category": "objects", "moji": "🎚", + "description": "level slider", "unicodeVersion": "7.0", "digest": "48842324f54d971ebf548a89a82ac7f29e235702081c91b477b1a92d427290e7" }, "levitate": { "category": "activity", "moji": "🕴", + "description": "man in business suit levitating", "unicodeVersion": "7.0", "digest": "453c24bf2544ed3ef3c710a7fabbd5fdace4dc65cddd377274d30d921523b50b" }, "libra": { "category": "symbols", "moji": "♎", + "description": "libra", "unicodeVersion": "1.1", "digest": "e330ba05bb449db074bc23d1514246ca5e249110f44ddb5804e5510eef6deac1" }, "lifter": { "category": "activity", "moji": "🏋", + "description": "weight lifter", "unicodeVersion": "7.0", "digest": "d6c94a32eb863d14a2a01add8ab95040f42a55d9e3f90641a0fe143d58127558" }, "lifter_tone1": { "category": "activity", "moji": "🏋🏻", + "description": "weight lifter tone 1", "unicodeVersion": "8.0", "digest": "870acf2f554fce360b58d3e98b4c0558d7ec7775587776c0f9d40c6fb1bdacf9" }, "lifter_tone2": { "category": "activity", "moji": "🏋🏼", + "description": "weight lifter tone 2", "unicodeVersion": "8.0", "digest": "1a7ece8512e42241cdd95c85ccc509bc0ff9c7c6ffaff2be343c77f417a27576" }, "lifter_tone3": { "category": "activity", "moji": "🏋🏽", + "description": "weight lifter tone 3", "unicodeVersion": "8.0", "digest": "4bc633ee82a0fb59feba379fb6901a489e4ac849d758f9c8e7a1a0a26eaa380c" }, "lifter_tone4": { "category": "activity", "moji": "🏋🏾", + "description": "weight lifter tone 4", "unicodeVersion": "8.0", "digest": "d086fe5577b5ba80676f2224d886f8ebe4588314f429f12a34c52c971ed71b5c" }, "lifter_tone5": { "category": "activity", "moji": "🏋🏿", + "description": "weight lifter tone 5", "unicodeVersion": "8.0", "digest": "79b0edf6ce1fd024dd7f458e322ad8588af0b789a04cc1cf38380dc8b9c76f55" }, "light_rail": { "category": "travel", "moji": "🚈", + "description": "light rail", "unicodeVersion": "6.0", "digest": "2f30b23a738371690b2f00d96ddb5ceb90a1442b5478754626a3dfa263ed2fc1" }, "link": { "category": "objects", "moji": "🔗", + "description": "link symbol", "unicodeVersion": "6.0", "digest": "7bf567aabd1fc38b3d70422f9db3a13b50950cf6207e70962c9938827c196ccb" }, "lion_face": { "category": "nature", "moji": "🦁", + "description": "lion face", "unicodeVersion": "8.0", "digest": "dd24f2668e973ec973e97dc111f59a2cc14e9b608387401191dd53368d28d4fa" }, "lips": { "category": "people", "moji": "👄", + "description": "mouth", "unicodeVersion": "6.0", "digest": "8740d8086525c7a836d64625a6915cc1c59af69ba143456dbb59e0179276895e" }, "lipstick": { "category": "people", "moji": "💄", + "description": "lipstick", "unicodeVersion": "6.0", "digest": "751dcb22706a796033b13a2ccb94304236ec13207ad4d011e02d230ae33ab5c1" }, "lizard": { "category": "nature", "moji": "🦎", + "description": "lizard", "unicodeVersion": "9.0", "digest": "fb9191f9eab58b8403d4c4626ccbb14ba05c1f6944011751a8edcc4dd03c66e6" }, "lock": { "category": "objects", "moji": "🔒", + "description": "lock", "unicodeVersion": "6.0", "digest": "043b4fc0b8c79d47a07d91308e628e1ac262aea6c1ec05e6b84bf7bcdf89dc83" }, "lock_with_ink_pen": { "category": "objects", "moji": "🔏", + "description": "lock with ink pen", "unicodeVersion": "6.0", "digest": "7b5e959b26cf7296c7b230fc2be9feb9e38391c5001951a019d16b169a71aba9" }, "lollipop": { "category": "food", "moji": "🍭", + "description": "lollipop", "unicodeVersion": "6.0", "digest": "17b6a0df47ec758a2f9c087b46a6902cee344d39407ef4c321e408505cbb72ca" }, "loop": { "category": "symbols", "moji": "➿", + "description": "double curly loop", "unicodeVersion": "6.0", "digest": "9f20ecc34b3c871789ba7d0712aa31e7a74b6c1558ac8bea385bc40590056726" }, "loud_sound": { "category": "symbols", "moji": "🔊", + "description": "speaker with three sound waves", "unicodeVersion": "6.0", "digest": "64b12db9ddd8adf74a9fc2bd83c7979ea865113347f7ce8666e9ccf5019e715f" }, "loudspeaker": { "category": "symbols", "moji": "📢", + "description": "public address loudspeaker", "unicodeVersion": "6.0", "digest": "1e1f35d16dd2898ebaa6f2b2868203df6e09c8a70df069c92d6d1b5cb2ac0976" }, "love_hotel": { "category": "travel", "moji": "🏩", + "description": "love hotel", "unicodeVersion": "6.0", "digest": "ff8966a50fd47a216855488eb09a367d231fea21f49e7e5325191d32fb494473" }, "love_letter": { "category": "objects", "moji": "💌", + "description": "love letter", "unicodeVersion": "6.0", "digest": "037261c8ca4d72f7205e51664591696da2ae7ceb19f1c1c9f6123da5a5979d29" }, "low_brightness": { "category": "symbols", "moji": "🔅", + "description": "low brightness symbol", "unicodeVersion": "6.0", "digest": "a065d00a416e297c168b0a675cafcf492fedf94865cb21801a1be5a3914593d4" }, "lying_face": { "category": "people", "moji": "🤥", + "description": "lying face", "unicodeVersion": "9.0", "digest": "ce836170165e1b70938273f289c02c2106873cd9ab5472dbcd487c2f9f53f13d" }, "m": { "category": "symbols", "moji": "Ⓜ", + "description": "circled latin capital letter m", "unicodeVersion": "1.1", "digest": "54588ac2b7fcd53a96f17124e9de69b617613fcd5af9ad2930a094cb795bb9f4" }, "mag": { "category": "objects", "moji": "🔍", + "description": "left-pointing magnifying glass", "unicodeVersion": "6.0", "digest": "a6e31a2efa7d9427aaa30b45d9f4181ee55c44be08aea2df165a86e0e6d9eaa1" }, "mag_right": { "category": "objects", "moji": "🔎", + "description": "right-pointing magnifying glass", "unicodeVersion": "6.0", "digest": "c7d8ceeb05db261e5eaab31dc4da432d0d5592a2ed71e526c5a542daa230bbaf" }, "mahjong": { "category": "symbols", "moji": "🀄", + "description": "mahjong tile red dragon", "unicodeVersion": "5.1", "digest": "755d69f988434ce1c17531a8b7ac92ead6f5607c2635a22f10e0ad70f09fc3e6" }, "mailbox": { "category": "objects", "moji": "📫", + "description": "closed mailbox with raised flag", "unicodeVersion": "6.0", "digest": "2069091be90a530a43ef29d5ec7688c351bf4d5b08d63a0d20d72b67d639ec62" }, "mailbox_closed": { "category": "objects", "moji": "📪", + "description": "closed mailbox with lowered flag", "unicodeVersion": "6.0", "digest": "d88d65bfebb8216535fd055c69f319564b2cf0b0901820f8312f581864557ed4" }, "mailbox_with_mail": { "category": "objects", "moji": "📬", + "description": "open mailbox with raised flag", "unicodeVersion": "6.0", "digest": "69e966b4659128991a70c6a2dd4d647551bedb91bdf5ce688958686bbec56381" }, "mailbox_with_no_mail": { "category": "objects", "moji": "📭", + "description": "open mailbox with lowered flag", "unicodeVersion": "6.0", "digest": "9e92d8ee88f660ce56da61077c80ec26c5d8f54ebd2306c4cfa16f6c1b981f83" }, "man": { "category": "people", "moji": "👨", + "description": "man", "unicodeVersion": "6.0", "digest": "42b882d2c6aa095f1afcf901203838d95c1908bdc725519779186b9c33c728d7" }, "man_dancing": { "category": "people", "moji": "🕺", + "description": "man dancing", "unicodeVersion": "9.0", "digest": "9f632ee0c886d5f03c61e5f3a27668262c0cc2693b857a91c23c1e5ea3785b9e" }, "man_dancing_tone1": { "category": "activity", "moji": "🕺🏻", + "description": "man dancing tone 1", "unicodeVersion": "9.0", "digest": "6c56a16cb105bcdd97472645b3a351cebdbb1132cbfd18b0118f289db5fbe741" }, "man_dancing_tone2": { "category": "activity", "moji": "🕺🏼", + "description": "man dancing tone 2", "unicodeVersion": "9.0", "digest": "ed7e78c14d205a03fdd5581e5213add69a55e13b4cbaf76a6d5a0d6c80f53327" }, "man_dancing_tone3": { "category": "activity", "moji": "🕺🏽", + "description": "man dancing tone 3", "unicodeVersion": "9.0", "digest": "13b45403e11800163406206eedeb8b579cc83eca2f60246be97e099164387bc8" }, "man_dancing_tone4": { "category": "activity", "moji": "🕺🏾", + "description": "man dancing tone 4", "unicodeVersion": "9.0", "digest": "f6feb1b0b83565fadcdd1a8737d3daa08893e919547d2a06de899160162d9c4a" }, "man_dancing_tone5": { "category": "activity", "moji": "🕺🏿", + "description": "man dancing tone 5", "unicodeVersion": "9.0", "digest": "fe20a9ed9ba991653b4d0683de347ed7c226a5d75610307584a2ddd6fcd1e3f2" }, "man_in_tuxedo": { "category": "people", "moji": "🤵", + "description": "man in tuxedo", "unicodeVersion": "9.0", "digest": "4d451a971dfefedc4830ba78e19b123f250e09ae65baddccdc56c0f8aa3a9b50" }, "man_in_tuxedo_tone1": { "category": "people", "moji": "🤵🏻", + "description": "man in tuxedo tone 1", "unicodeVersion": "9.0", "digest": "2814833334fb211ae2ecb1fb5964e9752282d0fb4d7f3477de5dd2a4f812a793" }, "man_in_tuxedo_tone2": { "category": "people", "moji": "🤵🏼", + "description": "man in tuxedo tone 2", "unicodeVersion": "9.0", "digest": "cd1bab9ee0e2335d3cd99d51216cccdc4fc3c2cf20129b8b7e11a51a77258f68" }, "man_in_tuxedo_tone3": { "category": "people", "moji": "🤵🏽", + "description": "man in tuxedo tone 3", "unicodeVersion": "9.0", "digest": "f387775f925fe60b9f3e7cad63a55d4d196ddd41658029a70440d14c17cb99f9" }, "man_in_tuxedo_tone4": { "category": "people", "moji": "🤵🏾", + "description": "man in tuxedo tone 4", "unicodeVersion": "9.0", "digest": "08debd7a573d1201aee8a2f281ef7cb638d4a2a096222150391f36963f07c622" }, "man_in_tuxedo_tone5": { "category": "people", "moji": "🤵🏿", + "description": "man in tuxedo tone 5", "unicodeVersion": "9.0", "digest": "e3b10e0619f0911cf9b665a265f4ef829b8f6ba6e9c3a021d0539a27e315f8fe" }, "man_tone1": { "category": "people", "moji": "👨🏻", + "description": "man tone 1", "unicodeVersion": "8.0", "digest": "7053e265fa7d2594de54a6c5d06c21795b9a7dfb36a1c5594ca43c4c6cc56504" }, "man_tone2": { "category": "people", "moji": "👨🏼", + "description": "man tone 2", "unicodeVersion": "8.0", "digest": "7ebc64de40d3ac60fb761be5cf94f53fa10b4f03fb66add46c90f5d98eaf71eb" }, "man_tone3": { "category": "people", "moji": "👨🏽", + "description": "man tone 3", "unicodeVersion": "8.0", "digest": "77ceef4d3740ed4751acb83dd45b6b754cf625c522c6757309cd4d61202d7149" }, "man_tone4": { "category": "people", "moji": "👨🏾", + "description": "man tone 4", "unicodeVersion": "8.0", "digest": "41e6037c393f61cca61b9a81b27ed14a95d75fe380e3a00153c33a371a836ffd" }, "man_tone5": { "category": "people", "moji": "👨🏿", + "description": "man tone 5", "unicodeVersion": "8.0", "digest": "a8cebfd39a5b9c79af7cc37f205e1135376056fee287af967c9f55d415572d99" }, "man_with_gua_pi_mao": { "category": "people", "moji": "👲", + "description": "man with gua pi mao", "unicodeVersion": "6.0", "digest": "3dae285e900c69986a48db0fa89d4f371a49f38608059cdae52be098030c5ac4" }, "man_with_gua_pi_mao_tone1": { "category": "people", "moji": "👲🏻", + "description": "man with gua pi mao tone 1", "unicodeVersion": "8.0", "digest": "35404d8e266920c78edd9e7143fb052b42f65242a5698494c4f4365e9183cc67" }, "man_with_gua_pi_mao_tone2": { "category": "people", "moji": "👲🏼", + "description": "man with gua pi mao tone 2", "unicodeVersion": "8.0", "digest": "82d4f968665a93c7543372c8a1eeb0f25d0ea6842d5e518bd91c226c6c3ab8c2" }, "man_with_gua_pi_mao_tone3": { "category": "people", "moji": "👲🏽", + "description": "man with gua pi mao tone 3", "unicodeVersion": "8.0", "digest": "f44159f0c672b9b833449382896180e799abf574f5b3c6cd9541caa992fa18ce" }, "man_with_gua_pi_mao_tone4": { "category": "people", "moji": "👲🏾", + "description": "man with gua pi mao tone 4", "unicodeVersion": "8.0", "digest": "c79060188f9461ca34eaa225b7682d8c410883609509fb731c992db69bfeeb50" }, "man_with_gua_pi_mao_tone5": { "category": "people", "moji": "👲🏿", + "description": "man with gua pi mao tone 5", "unicodeVersion": "8.0", "digest": "de9e4acdb10f7abddeeabc0b48d91139fc8b544a601c530db811f099991b0d38" }, "man_with_turban": { "category": "people", "moji": "👳", + "description": "man with turban", "unicodeVersion": "6.0", "digest": "db72c944e93983f38d00e3e936ebb5b243c6069f1f1236d46f6a9f1beb8d6634" }, "man_with_turban_tone1": { "category": "people", "moji": "👳🏻", + "description": "man with turban tone 1", "unicodeVersion": "8.0", "digest": "b6d7489c4cd151af09fff48b62c54c336303e14866e6ef38f94cd834b085d09e" }, "man_with_turban_tone2": { "category": "people", "moji": "👳🏼", + "description": "man with turban tone 2", "unicodeVersion": "8.0", "digest": "7854ef973c21847f452d7e78e5c460ea300e12b539ce92c69dabe8f1bf3a4382" }, "man_with_turban_tone3": { "category": "people", "moji": "👳🏽", + "description": "man with turban tone 3", "unicodeVersion": "8.0", "digest": "1dbd9bd78f5263cbadee7d0d5754c14cfbc914f7329e25fbd97d9f5b8ce0737e" }, "man_with_turban_tone4": { "category": "people", "moji": "👳🏾", + "description": "man with turban tone 4", "unicodeVersion": "8.0", "digest": "4f4804da4a7c98ad4f9db3ae3eaf674c8977c638e73414e33ef1f65098e413a3" }, "man_with_turban_tone5": { "category": "people", "moji": "👳🏿", + "description": "man with turban tone 5", "unicodeVersion": "8.0", "digest": "240282aa346ef9b1d0d475ea93a02597697f0f56f086305879b532b0b933210a" }, "mans_shoe": { "category": "people", "moji": "👞", + "description": "mans shoe", "unicodeVersion": "6.0", "digest": "f53fe74abd9906cd3e2dd7e7bddbe1feb9f8f7be28b807fabe452f1f60ca1b84" }, "map": { "category": "objects", "moji": "🗺", + "description": "world map", "unicodeVersion": "7.0", "digest": "84f496a062b5c3ae1e8013506175a69036038c8130891bcf780a69ce7fcbe4de" }, "maple_leaf": { "category": "nature", "moji": "🍁", + "description": "maple leaf", "unicodeVersion": "6.0", "digest": "72629a205e33f89337815ad7e51bb5c73947d1a9f98afe5072bdf4846827ae72" }, "martial_arts_uniform": { "category": "activity", "moji": "🥋", + "description": "martial arts uniform", "unicodeVersion": "9.0", "digest": "a1ae797b31081425b388ab31efc635d8eb73a40980fd0fae4708aa5313e2a964" }, "mask": { "category": "people", "moji": "😷", + "description": "face with medical mask", "unicodeVersion": "6.0", "digest": "1b58af9ae599308aabf41bbd38f599fa896bd9fe5df7a40be9f2dc7e0e230600" }, "massage": { "category": "people", "moji": "💆", + "description": "face massage", "unicodeVersion": "6.0", "digest": "6ee48b4d8cec0bf31e11d7803ad9fc1f909457c8c00cb320b5671395af3c170c" }, "massage_tone1": { "category": "people", "moji": "💆🏻", + "description": "face massage tone 1", "unicodeVersion": "8.0", "digest": "9da162c2f39628156b87db986a6ada59372a9e9a6b3f0488d21c9e65ec3309bb" }, "massage_tone2": { "category": "people", "moji": "💆🏼", + "description": "face massage tone 2", "unicodeVersion": "8.0", "digest": "ac259188549b5b429b8c4929e1da2314859e8857ee49720551467aedfcc96567" }, "massage_tone3": { "category": "people", "moji": "💆🏽", + "description": "face massage tone 3", "unicodeVersion": "8.0", "digest": "cfd9c105b6debc10448f172afcb20d4192899f7ae5aa8af54c834153a5466364" }, "massage_tone4": { "category": "people", "moji": "💆🏾", + "description": "face massage tone 4", "unicodeVersion": "8.0", "digest": "38ab715c621c58454f3cb09153a96380118cf082568554b6edc5f83fb62e9297" }, "massage_tone5": { "category": "people", "moji": "💆🏿", + "description": "face massage tone 5", "unicodeVersion": "8.0", "digest": "32480457734121b0c83e9be6d693ae379c95535f43f963c0c2f0f20434ee12c6" }, "meat_on_bone": { "category": "food", "moji": "🍖", + "description": "meat on bone", "unicodeVersion": "6.0", "digest": "d71a8e0b118d5e6ca60690793ce9649afb78e707fcbd7be890a75564c94434fd" }, "medal": { "category": "activity", "moji": "🏅", + "description": "sports medal", "unicodeVersion": "7.0", "digest": "9600cbe57e08da090c60629bcafd2821c87322e738c2454f8e883ceb756e7391" }, "mega": { "category": "symbols", "moji": "📣", + "description": "cheering megaphone", "unicodeVersion": "6.0", "digest": "4b1def6b5b051c5045514063f0ac006222ad81fbfe56d840e14bb950713e331b" }, "melon": { "category": "food", "moji": "🍈", + "description": "melon", "unicodeVersion": "6.0", "digest": "0cdd663e6f2129808856cdf0746e6571b62aac641f224adb553baf3bb63ba3bd" }, "menorah": { "category": "symbols", "moji": "🕎", + "description": "menorah with nine branches", "unicodeVersion": "8.0", "digest": "49fca8c3bc00ea69653ee2f8d4e21e561856ba39716c13e9d107db3e805a2997" }, "mens": { "category": "symbols", "moji": "🚹", + "description": "mens symbol", "unicodeVersion": "6.0", "digest": "7d92292586ee12a5d1a557c37da4d14708dc3ce701cf32d3280dcc83d91e5df8" }, "metal": { "category": "people", "moji": "🤘", + "description": "sign of the horns", "unicodeVersion": "8.0", "digest": "ffb750caf187f5d821c990108e2699ac3e216492bcff6ee543f4a7aa55b9fd29" }, "metal_tone1": { "category": "people", "moji": "🤘🏻", + "description": "sign of the horns tone 1", "unicodeVersion": "8.0", "digest": "5505f0b0340f9ba572db8897e40adf598cfa784686ad5ee360a7351bf44ddc1d" }, "metal_tone2": { "category": "people", "moji": "🤘🏼", + "description": "sign of the horns tone 2", "unicodeVersion": "8.0", "digest": "8f9eee3ad5fc7eeeb30118d16d27467b16fd87297e0ecf02656db77e701f5aeb" }, "metal_tone3": { "category": "people", "moji": "🤘🏽", + "description": "sign of the horns tone 3", "unicodeVersion": "8.0", "digest": "8270a7ecf5eb11431a07ef04cc476c2651ac8aacb0d4768e5cb69355f8a5e84e" }, "metal_tone4": { "category": "people", "moji": "🤘🏾", + "description": "sign of the horns tone 4", "unicodeVersion": "8.0", "digest": "f24f7b137dd6c7899dc0a8794204bbde7ad43ec1e63b419c90dd70a8b77871e8" }, "metal_tone5": { "category": "people", "moji": "🤘🏿", + "description": "sign of the horns tone 5", "unicodeVersion": "8.0", "digest": "07b0726a632653b980df775f460cd3fe1ea8d4a7b0b46fe29e089b66579482d2" }, "metro": { "category": "travel", "moji": "🚇", + "description": "metro", "unicodeVersion": "6.0", "digest": "b380247b61b5e2ca1b9b70fabff65907b2c3a5191a14b169ae094af94659b9b1" }, "microphone": { "category": "activity", "moji": "🎤", + "description": "microphone", "unicodeVersion": "6.0", "digest": "9ef4fc2e40d5391c4bb2d30f34f59662cff7cbb1b04341c9dac210d0e21b44ae" }, "microphone2": { "category": "objects", "moji": "🎙", + "description": "studio microphone", "unicodeVersion": "7.0", "digest": "8a30464d51f7f101335778444c43270ac0679900f49463e6556682d9db1cb4dc" }, "microscope": { "category": "objects", "moji": "🔬", + "description": "microscope", "unicodeVersion": "6.0", "digest": "4ca4322c6ba99b8c15acdb8b605f84f87398769e504b262b134c1f3868b2692f" }, "middle_finger": { "category": "people", "moji": "🖕", + "description": "reversed hand with middle finger extended", "unicodeVersion": "7.0", "digest": "0c3f1cc0ec7323f6d19508ad22fa90050845f7b5cc83f599ab2cacb89cf5dd0e" }, "middle_finger_tone1": { "category": "people", "moji": "🖕🏻", + "description": "reversed hand with middle finger extended tone 1", "unicodeVersion": "8.0", "digest": "4ebecf1058a3059aaa826eaad39c1a791120f115f65dde6d6ae32fc5561f60f7" }, "middle_finger_tone2": { "category": "people", "moji": "🖕🏼", + "description": "reversed hand with middle finger extended tone 2", "unicodeVersion": "8.0", "digest": "85ff506a08c38663c2dfa2e3a90584c02a36aa3dda33af47cdb49834bf9baf83" }, "middle_finger_tone3": { "category": "people", "moji": "🖕🏽", + "description": "reversed hand with middle finger extended tone 3", "unicodeVersion": "8.0", "digest": "cac697ff5207bf8a4e091912f3127f4e73c88ef69b5c6561d1d7b12ed60be8f1" }, "middle_finger_tone4": { "category": "people", "moji": "🖕🏾", + "description": "reversed hand with middle finger extended tone 4", "unicodeVersion": "8.0", "digest": "9324a5a4e3986b798ad8c61f31c18fb507ca7a4abfd6e9ae1408b80b185bf8c7" }, "middle_finger_tone5": { "category": "people", "moji": "🖕🏿", + "description": "reversed hand with middle finger extended tone 5", "unicodeVersion": "8.0", "digest": "078f917cd4d8be08a880724e9400449980d92740ccbee4a57f5046a9cf7f6575" }, "military_medal": { "category": "activity", "moji": "🎖", + "description": "military medal", "unicodeVersion": "7.0", "digest": "5da18351dc14b66cfc070148c83b7c8e67e6b1e3f515ae501133c38ee5c28d3d" }, "milk": { "category": "food", "moji": "🥛", + "description": "glass of milk", "unicodeVersion": "9.0", "digest": "38b28ea40399601fabc95bac5eaaf5a9e4e25548ec80325bd5069395ea884f85" }, "milky_way": { "category": "travel", "moji": "🌌", + "description": "milky way", "unicodeVersion": "6.0", "digest": "17405ff31d94b13a1fb0adcda204b8adb95ca340bc3980d9ad9f42ba1e366e7d" }, "minibus": { "category": "travel", "moji": "🚐", + "description": "minibus", "unicodeVersion": "6.0", "digest": "08ccb4b1bf397b7c9aed901e2b5dcdd6cb8ca5c5487ef26775bb3120f7b92524" }, "minidisc": { "category": "objects", "moji": "💽", + "description": "minidisc", "unicodeVersion": "6.0", "digest": "bebf82c0b91ef66321e7ae7a0abf322e59b2f7d8e6fbf9a94243210c00229c59" }, "mobile_phone_off": { "category": "symbols", "moji": "📴", + "description": "mobile phone off", "unicodeVersion": "6.0", "digest": "6f9d8d6a32fc998f5d8144a5ff7e2ad00de37ad464cd97285e7c72efb09a1feb" }, "money_mouth": { "category": "people", "moji": "🤑", + "description": "money-mouth face", "unicodeVersion": "8.0", "digest": "5a43973dadf48a89201b1816fea9972c5cfe501a26fe457b6f7eee0a6362018e" }, "money_with_wings": { "category": "objects", "moji": "💸", + "description": "money with wings", "unicodeVersion": "6.0", "digest": "15fcf0595021374ba091ca00efdb4167770da4d421eab930964108545f4edab9" }, "moneybag": { "category": "objects", "moji": "💰", + "description": "money bag", "unicodeVersion": "6.0", "digest": "02d708e2f603b0df6f6c169b5c49b3452e1c02e7d72e96f228b73d0b0a20bff4" }, "monkey": { "category": "nature", "moji": "🐒", + "description": "monkey", "unicodeVersion": "6.0", "digest": "3588a544d6d9e9995b45d60327a1a42002fa1faa4d48224b140facd249af1c67" }, "monkey_face": { "category": "nature", "moji": "🐵", + "description": "monkey face", "unicodeVersion": "6.0", "digest": "9e263ef5ca42bb76d1b1d1e3cbf020bcf05023a6e9f91301d30c9eb406363a2a" }, "monorail": { "category": "travel", "moji": "🚝", + "description": "monorail", "unicodeVersion": "6.0", "digest": "2c9f185babcb4001fcef2b8dfc4a32126729843084d0076c3e3ccdc845ab23ad" }, "mortar_board": { "category": "people", "moji": "🎓", + "description": "graduation cap", "unicodeVersion": "6.0", "digest": "d7fbe41d4b340d3564e484aec46a22c9613521414b2ba6eece2180db4d23e410" }, "mosque": { "category": "travel", "moji": "🕌", + "description": "mosque", "unicodeVersion": "8.0", "digest": "5f3d3de7feac953a70a318113531c2857d760a516c3d8d6f42d2a3b3b67ed196" }, "motor_scooter": { "category": "travel", "moji": "🛵", + "description": "motor scooter", "unicodeVersion": "9.0", "digest": "e2dc7c981744a71f46858bd0858ff91af704ac06425ed80377bc3b119e57c872" }, "motorboat": { "category": "travel", "moji": "🛥", + "description": "motorboat", "unicodeVersion": "7.0", "digest": "81c156643528c5a94a12d6d478e52a019f5a4e3eb58ee365cdd9d2361a7fdb01" }, "motorcycle": { "category": "travel", "moji": "🏍", + "description": "racing motorcycle", "unicodeVersion": "7.0", "digest": "354aa8157732184ad50eff9330f7a8915309dc9b7893cc308226adb429311a62" }, "motorway": { "category": "travel", "moji": "🛣", + "description": "motorway", "unicodeVersion": "7.0", "digest": "148c3c13c7c4565453d16e504e0d4b8d007e4f2cad1ab56b1b51fefe39162d17" }, "mount_fuji": { "category": "travel", "moji": "🗻", + "description": "mount fuji", "unicodeVersion": "6.0", "digest": "f8093b9dba62b22c6c88f137be88b2fd3971c560714db15ec053cf697a3820bc" }, "mountain": { "category": "travel", "moji": "⛰", + "description": "mountain", "unicodeVersion": "5.2", "digest": "07423804ad79da68f140948d29df193f5d5343b7b2c23758c086697c4d3a50da" }, "mountain_bicyclist": { "category": "activity", "moji": "🚵", + "description": "mountain bicyclist", "unicodeVersion": "6.0", "digest": "91084b6c887cb7e34f3d7ec30656ecb82c36cc987f53a6c83ccb4c6f7950f96a" }, "mountain_bicyclist_tone1": { "category": "activity", "moji": "🚵🏻", + "description": "mountain bicyclist tone 1", "unicodeVersion": "8.0", "digest": "5d57fcfad61bca26c3e8965eb57602a1993a3117ebdda0f24569af730310ab6e" }, "mountain_bicyclist_tone2": { "category": "activity", "moji": "🚵🏼", + "description": "mountain bicyclist tone 2", "unicodeVersion": "8.0", "digest": "c0da7fb85d99aa01a665f64063cd7e2d994f8a16d3f6fbf52df5d471e771a98a" }, "mountain_bicyclist_tone3": { "category": "activity", "moji": "🚵🏽", + "description": "mountain bicyclist tone 3", "unicodeVersion": "8.0", "digest": "b099e7ee84eae44ebc99023fa06bdf37ffa0d69767c7c0163a89f7ced2a26765" }, "mountain_bicyclist_tone4": { "category": "activity", "moji": "🚵🏾", + "description": "mountain bicyclist tone 4", "unicodeVersion": "8.0", "digest": "9d09f7b3899ea44e736f237a161ef8d5170dccfa162a872c59532ceaf65ee007" }, "mountain_bicyclist_tone5": { "category": "activity", "moji": "🚵🏿", + "description": "mountain bicyclist tone 5", "unicodeVersion": "8.0", "digest": "71e374981d955056748a60c6d1820b45e9688a156b55318b4ea54a3a67ca801c" }, "mountain_cableway": { "category": "travel", "moji": "🚠", + "description": "mountain cableway", "unicodeVersion": "6.0", "digest": "e261c3292758b1c0063c5a0d0c7f5c9803306d2265e08677027e1210506ced94" }, "mountain_railway": { "category": "travel", "moji": "🚞", + "description": "mountain railway", "unicodeVersion": "6.0", "digest": "b0987f8f391b3cbc7a56b9b8945ebfca240e01d12f8fd163877ebebe51d6b277" }, "mountain_snow": { "category": "travel", "moji": "🏔", + "description": "snow capped mountain", "unicodeVersion": "7.0", "digest": "49aac2b851aa6f2bd2ca641efa8060f93e89395357f49d211658d46f5a2b0189" }, "mouse": { "category": "nature", "moji": "🐭", + "description": "mouse face", "unicodeVersion": "6.0", "digest": "007dd108507b45224f7a1fad3c1de6ecc75f38d71fc142744611eb13555f5eff" }, "mouse2": { "category": "nature", "moji": "🐁", + "description": "mouse", "unicodeVersion": "6.0", "digest": "f3ed37b639b7c16aae49502bd423f9fdeabaf15bc6f0f74063954b189e176b5d" }, "mouse_three_button": { "category": "objects", "moji": "🖱", + "description": "three button mouse", "unicodeVersion": "7.0", "digest": "3724341ac5ad0d01027ef1575db64f1db7619f590ca6ada960d1f2c18dc7fc6a" }, "movie_camera": { "category": "objects", "moji": "🎥", + "description": "movie camera", "unicodeVersion": "6.0", "digest": "f7e285eda35b4431c07951e071643ddc34147cd76640e0d516fbfd11208346e9" }, "moyai": { "category": "objects", "moji": "🗿", + "description": "moyai", "unicodeVersion": "6.0", "digest": "2c1d0662c95928936e6b9ab5a40c6110ff1cea5339f2803c7b63aabc76115afb" }, "mrs_claus": { "category": "people", "moji": "🤶", + "description": "mother christmas", "unicodeVersion": "9.0", "digest": "1f72f586ca75bd7ebb4150cdcc8199a930c32fa4b81510cb8d200f1b3ddd4076" }, "mrs_claus_tone1": { "category": "people", "moji": "🤶🏻", + "description": "mother christmas tone 1", "unicodeVersion": "9.0", "digest": "244596919e0fed050203cf9e040899de323d7821235929f175852439927bd129" }, "mrs_claus_tone2": { "category": "people", "moji": "🤶🏼", + "description": "mother christmas tone 2", "unicodeVersion": "9.0", "digest": "8cde96e8521f3a90262a7f5f8a2989a9590d9a02cda2c37e92335dc05975c18d" }, "mrs_claus_tone3": { "category": "people", "moji": "🤶🏽", + "description": "mother christmas tone 3", "unicodeVersion": "9.0", "digest": "c39cd4346d4581799dd0e9a6447c91a954a75747bf2682c8e4d79c3b0fcf7405" }, "mrs_claus_tone4": { "category": "people", "moji": "🤶🏾", + "description": "mother christmas tone 4", "unicodeVersion": "9.0", "digest": "84c85cf54559ea2d78d196fee96149a249af4f959b78e223a0ec4fb72abdbcab" }, "mrs_claus_tone5": { "category": "people", "moji": "🤶🏿", + "description": "mother christmas tone 5", "unicodeVersion": "9.0", "digest": "ce26c0e0645713b17e7497d9f2d0484cc5477564dae99320cabf04d160d3b2ff" }, "muscle": { "category": "people", "moji": "💪", + "description": "flexed biceps", "unicodeVersion": "6.0", "digest": "e4ce52757b2b7982e2516e0e8bf2e2253617cc9f3e6178f1887c61c9039461ba" }, "muscle_tone1": { "category": "people", "moji": "💪🏻", + "description": "flexed biceps tone 1", "unicodeVersion": "8.0", "digest": "4a2fa226a05bb847b62cdd163eb6c2d514d3c2330a727991cf550c0d32b0e818" }, "muscle_tone2": { "category": "people", "moji": "💪🏼", + "description": "flexed biceps tone 2", "unicodeVersion": "8.0", "digest": "a8d5ecce335c782ca5f5e55763c06cfefa1c16c24cd6602237cf125d4ff95e47" }, "muscle_tone3": { "category": "people", "moji": "💪🏽", + "description": "flexed biceps tone 3", "unicodeVersion": "8.0", "digest": "070354b443faec3969663b770545fc4cf5ec75148557b2b9d6fc82ab22b43bd1" }, "muscle_tone4": { "category": "people", "moji": "💪🏾", + "description": "flexed biceps tone 4", "unicodeVersion": "8.0", "digest": "8eafcdb6a607aeafa673c257df0d2a1b20f00fc0868d811babcbe784490a0dd3" }, "muscle_tone5": { "category": "people", "moji": "💪🏿", + "description": "flexed biceps tone 5", "unicodeVersion": "8.0", "digest": "85a1e2b5c89907694240e9c5b9d876a741fa7ba38918c5718273e289cbc40efe" }, "mushroom": { "category": "nature", "moji": "🍄", + "description": "mushroom", "unicodeVersion": "6.0", "digest": "aaca8cf7c5cfa4487b5fef365a231f98be4bbf041197fc022161bcc8ce6f57c8" }, "musical_keyboard": { "category": "activity", "moji": "🎹", + "description": "musical keyboard", "unicodeVersion": "6.0", "digest": "fb0a726728900377d76d94aac9c94dce29107e8e3f1dcb0599d95bce7169b492" }, "musical_note": { "category": "symbols", "moji": "🎵", + "description": "musical note", "unicodeVersion": "6.0", "digest": "41288e79b4070bb980281d0e0d1c14d8b144b4aedb2eaadb9f2bebcb4ef892b4" }, "musical_score": { "category": "activity", "moji": "🎼", + "description": "musical score", "unicodeVersion": "6.0", "digest": "f0f91b9fa4a2bff7a5a1a11afa6f31cfe7e5fa8b0d6f3cce904b781a28ed0277" }, "mute": { "category": "symbols", "moji": "🔇", + "description": "speaker with cancellation stroke", "unicodeVersion": "6.0", "digest": "def277da49d744b55c7cdde269a15aa05315898f615e721ee7e9205d7b8030d6" }, "nail_care": { "category": "people", "moji": "💅", + "description": "nail polish", "unicodeVersion": "6.0", "digest": "48b33b1dbbd25b4f34ab2ca07bb99ddaaaa741990142c5623310f76b78c076f9" }, "nail_care_tone1": { "category": "people", "moji": "💅🏻", + "description": "nail polish tone 1", "unicodeVersion": "8.0", "digest": "a9ac92a34f407e7dd7c71377e6275e66657f7f42e4b911c540d1a66a02d92ac5" }, "nail_care_tone2": { "category": "people", "moji": "💅🏼", + "description": "nail polish tone 2", "unicodeVersion": "8.0", "digest": "f295ec85980aaa75818fad619c3d25042146ecbbf361db9e9bb96e7bc202bc73" }, "nail_care_tone3": { "category": "people", "moji": "💅🏽", + "description": "nail polish tone 3", "unicodeVersion": "8.0", "digest": "02ec373052a250977298bae85262177910126cc10de9480f1afa328ac2f65a95" }, "nail_care_tone4": { "category": "people", "moji": "💅🏾", + "description": "nail polish tone 4", "unicodeVersion": "8.0", "digest": "f3d95390ab59caedfda66122bbd0acf3aabedc142fc48352d68900766a7e6f5c" }, "nail_care_tone5": { "category": "people", "moji": "💅🏿", + "description": "nail polish tone 5", "unicodeVersion": "8.0", "digest": "009423c97f2aafd24fb8c7c485c58b30bbf9ae6797cc14b80d472b207327b518" }, "name_badge": { "category": "symbols", "moji": "📛", + "description": "name badge", "unicodeVersion": "6.0", "digest": "f9f6a4895ff0be8fb2ccc7ad195b94e9650f742f66ead999e90724cfb77af628" }, "nauseated_face": { "category": "people", "moji": "🤢", + "description": "nauseated face", "unicodeVersion": "9.0", "digest": "f8471cf4720948d8246ec9d30e29783e819f90e3cfe8b1ba628671a1aad1a91c" }, "necktie": { "category": "people", "moji": "👔", + "description": "necktie", "unicodeVersion": "6.0", "digest": "01bb18dc8bfe787daa9613b5d09988cd5a065449ef906099ce3cb308c8a7da68" }, "negative_squared_cross_mark": { "category": "symbols", "moji": "❎", + "description": "negative squared cross mark", "unicodeVersion": "6.0", "digest": "1cdaf4abc9adafa089c91c2e33a24e9e647aea0f857e767941a899a16ec53b74" }, "nerd": { "category": "people", "moji": "🤓", + "description": "nerd face", "unicodeVersion": "8.0", "digest": "9e5f3c93db25cf1d0f9d6e6bd2993161afec6c30573ba3fe85e13b8c84483d66" }, "neutral_face": { "category": "people", "moji": "😐", + "description": "neutral face", "unicodeVersion": "6.0", "digest": "7449430a60619956573e9dc80834045296f2b99853737b6c7794c785ff53d64e" }, "new": { "category": "symbols", "moji": "🆕", + "description": "squared new", "unicodeVersion": "6.0", "digest": "e20bc3e9f40726afd0cfb7268d02f1e1a07343364fd08b252d59f38de067bf06" }, "new_moon": { "category": "nature", "moji": "🌑", + "description": "new moon symbol", "unicodeVersion": "6.0", "digest": "dbfc5dcae34b45f15ff767e297cba3a12cb83f3b542db8cfc8dbd9669e0df46c" }, "new_moon_with_face": { "category": "nature", "moji": "🌚", + "description": "new moon with face", "unicodeVersion": "6.0", "digest": "c66d347d2222ac8d77d323a07699aff6b168328648db4f885b1ed0e2831fd59b" }, "newspaper": { "category": "objects", "moji": "📰", + "description": "newspaper", "unicodeVersion": "6.0", "digest": "c05e986d9cdac11afa30c6a21a72572ddf50fc64e87ae0c4e0ad57ffe70acc5c" }, "newspaper2": { "category": "objects", "moji": "🗞", + "description": "rolled-up newspaper", "unicodeVersion": "7.0", "digest": "63db7bcf51effc73e5124392740736383774a4bcfbc1156cf55599504760883d" }, "ng": { "category": "symbols", "moji": "🆖", + "description": "squared ng", "unicodeVersion": "6.0", "digest": "34d5a11c70f48ea719e602908534f446b192622e775d4160f0e1ec52c342a35c" }, "night_with_stars": { "category": "travel", "moji": "🌃", + "description": "night with stars", "unicodeVersion": "6.0", "digest": "39d9c079be80ee6ce1667531be528a2aa7f8bd46c7b6c2a6ee279d9a207c84a4" }, "nine": { "category": "symbols", "moji": "9️⃣", + "description": "keycap digit nine", "unicodeVersion": "3.0", "digest": "8bb40750eda8506ef877c9a3b8e2039d26f20eef345742f635740574a7e8daa6" }, "no_bell": { "category": "symbols", "moji": "🔕", + "description": "bell with cancellation stroke", "unicodeVersion": "6.0", "digest": "6542a9a5656c79c153f8c37f12d48f677c89b02ed0989ae37fa5e51ce6895422" }, "no_bicycles": { "category": "symbols", "moji": "🚳", + "description": "no bicycles", "unicodeVersion": "6.0", "digest": "af71c183545da2ff4c05609f9d572edb64b63ccba7c6a4b208d271558aa92b0a" }, "no_entry": { "category": "symbols", "moji": "⛔", + "description": "no entry", "unicodeVersion": "5.2", "digest": "dc0bac1ed9ab8e9af143f0fce5043fe68f7f46bd80856cdec95d20c3999b637d" }, "no_entry_sign": { "category": "symbols", "moji": "🚫", + "description": "no entry sign", "unicodeVersion": "6.0", "digest": "2c1fceef23b62effca68e0e087b8f020125d25b98d61492b1540055d1914fdc3" }, "no_good": { "category": "people", "moji": "🙅", + "description": "face with no good gesture", "unicodeVersion": "6.0", "digest": "6eb970b104389be5d18657d7c04be5149958c26855c52ea68574af852c5f85c4" }, "no_good_tone1": { "category": "people", "moji": "🙅🏻", + "description": "face with no good gesture tone 1", "unicodeVersion": "8.0", "digest": "c20a24a1e536240b4dcf90ecb530796de621d7ba1fb9e3fa0f849d048c509c03" }, "no_good_tone2": { "category": "people", "moji": "🙅🏼", + "description": "face with no good gesture tone 2", "unicodeVersion": "8.0", "digest": "f31a4628c1f2e6a39288fda8eb19c9ec89983e3726e17a09384d9ecc13ef0b4c" }, "no_good_tone3": { "category": "people", "moji": "🙅🏽", + "description": "face with no good gesture tone 3", "unicodeVersion": "8.0", "digest": "959dec1bfdaf37b20a86ab2bcbdbacd3179c87b163042377d966eab47564c0fb" }, "no_good_tone4": { "category": "people", "moji": "🙅🏾", + "description": "face with no good gesture tone 4", "unicodeVersion": "8.0", "digest": "efd931f0080adf2e04129c83a8b24fda0ae7a9fa7c4b463686c0b99023620db8" }, "no_good_tone5": { "category": "people", "moji": "🙅🏿", + "description": "face with no good gesture tone 5", "unicodeVersion": "8.0", "digest": "f35df2b26af9baef47c1f8cc97a1b28a58aa7fcb2a13fdac7b2d9189f1e40105" }, "no_mobile_phones": { "category": "symbols", "moji": "📵", + "description": "no mobile phones", "unicodeVersion": "6.0", "digest": "a472decd6ac7f9777961c09e00458746b2c04965585e3bee4556be3968e55bcd" }, "no_mouth": { "category": "people", "moji": "😶", + "description": "face without mouth", "unicodeVersion": "6.0", "digest": "72dda8b1e3ad4b05d9b095f9bd05e95d7ba013906c68914976a4554e8edf5866" }, "no_pedestrians": { "category": "symbols", "moji": "🚷", + "description": "no pedestrians", "unicodeVersion": "6.0", "digest": "062b4a71b338fe09775e465bfba8ac04efbb3640330e8cabe88f3af62b0f4225" }, "no_smoking": { "category": "symbols", "moji": "🚭", + "description": "no smoking symbol", "unicodeVersion": "6.0", "digest": "ae2ebb331f79f6074091c0ee9cd69fce16d5e12a131d18973fc05520097e14ee" }, "non-potable_water": { "category": "symbols", "moji": "🚱", + "description": "non-potable water symbol", "unicodeVersion": "6.0", "digest": "32eba0a99b498133c2e4450036f768d3dccaaf5b50adc9ad988757adc777a6a1" }, "nose": { "category": "people", "moji": "👃", + "description": "nose", "unicodeVersion": "6.0", "digest": "9f800e24658ea3cebe1144d5d808cf13a88261f1a7f1f81a10d03b3d9d00e541" }, "nose_tone1": { "category": "people", "moji": "👃🏻", + "description": "nose tone 1", "unicodeVersion": "8.0", "digest": "a2d0af22284b1d264eb780943b8360f463996a5c9c9584b8473edf8d442d9173" }, "nose_tone2": { "category": "people", "moji": "👃🏼", + "description": "nose tone 2", "unicodeVersion": "8.0", "digest": "244dcaa8540024cf521f29f36bd48f933bf82f4833e35e6fa0abf113022038f3" }, "nose_tone3": { "category": "people", "moji": "👃🏽", + "description": "nose tone 3", "unicodeVersion": "8.0", "digest": "c935b64866f0d49da52035aa09f36ff56d238eb7f5b92205386451056e8ea74f" }, "nose_tone4": { "category": "people", "moji": "👃🏾", + "description": "nose tone 4", "unicodeVersion": "8.0", "digest": "a87e95fd9319c49e66b6dea0e57319d0ed9921b8d94df037767bf3d5dc7c94f3" }, "nose_tone5": { "category": "people", "moji": "👃🏿", + "description": "nose tone 5", "unicodeVersion": "8.0", "digest": "1e0f9842e0f8ad5805eabd3f35a6038b7a2e49d566a1f5c17271f9cdf467ca60" }, "notebook": { "category": "objects", "moji": "📓", + "description": "notebook", "unicodeVersion": "6.0", "digest": "fc679d3728f86073d1607a926885dd8b0261132f5c4a0322f1e46ea9f95c8cb8" }, "notebook_with_decorative_cover": { "category": "objects", "moji": "📔", + "description": "notebook with decorative cover", "unicodeVersion": "6.0", "digest": "d822eda4b49cbfa399b36f134c1a0b8dcfdd27ed89f12c50bc18f6f0a9aa56ef" }, "notepad_spiral": { "category": "objects", "moji": "🗒", + "description": "spiral note pad", "unicodeVersion": "7.0", "digest": "c6a8e16aa62474cef13e5659fddb4afc57e3f79635e32e6020edbee2b5b50f18" }, "notes": { "category": "symbols", "moji": "🎶", + "description": "multiple musical notes", "unicodeVersion": "6.0", "digest": "98467e0adc134d45676ef1c6c459e5853a9db50c8a6e91b6aec7d449aa737f48" }, "nut_and_bolt": { "category": "objects", "moji": "🔩", + "description": "nut and bolt", "unicodeVersion": "6.0", "digest": "a77bd72f29a7302195dcec240174b15586de79e3204258e3fb401a6ea90563b3" }, "o": { "category": "symbols", "moji": "⭕", + "description": "heavy large circle", "unicodeVersion": "5.2", "digest": "2387e5fd9ae4c2972d40298d32319b8fa55c50dbfc1c04c5c36088213e6951dd" }, "o2": { "category": "symbols", "moji": "🅾", + "description": "negative squared latin capital letter o", "unicodeVersion": "6.0", "digest": "6a9ccb0bf394e4d05ffda19327cee18f7b9ed80367fc7f41c93da9bb7efab0bf" }, "ocean": { "category": "nature", "moji": "🌊", + "description": "water wave", "unicodeVersion": "6.0", "digest": "1a9ca9848d4fb75852addfc10bf84eccf7caa5339714b90e3de4cb6f2518465e" }, "octagonal_sign": { "category": "symbols", "moji": "🛑", + "description": "octagonal sign", "unicodeVersion": "9.0", "digest": "9f6927048e1f9da57f89d1ae1eb86fa4ab7abdbabca756a738a799e948d0b3f9" }, "octopus": { "category": "nature", "moji": "🐙", + "description": "octopus", "unicodeVersion": "6.0", "digest": "0fcc65c12f4b29ea75a8c4823d20838a7e6db6978fdcb536943072aa1460bc59" }, "oden": { "category": "food", "moji": "🍢", + "description": "oden", "unicodeVersion": "6.0", "digest": "089974cb13a0bef6a245fc73029c5ed5153fd4caae0177b835f779e32200b8aa" }, "office": { "category": "travel", "moji": "🏢", + "description": "office building", "unicodeVersion": "6.0", "digest": "3633a2e91036362e273eef4e0cfbdbbb4cb1208afe2cfa110ebef7b78109a66f" }, "oil": { "category": "objects", "moji": "🛢", + "description": "oil drum", "unicodeVersion": "7.0", "digest": "00b94d33bcc9b9e8a5d4bd6e7f7e2fced9497ce05919edd5e58eafbc011c2caa" }, "ok": { "category": "symbols", "moji": "🆗", + "description": "squared ok", "unicodeVersion": "6.0", "digest": "5f320f9b96e98a2f17ebe240daff9b9fd2ae0727cd6c8e4633b1744356e89365" }, "ok_hand": { "category": "people", "moji": "👌", + "description": "ok hand sign", "unicodeVersion": "6.0", "digest": "d63002dce3cc3655b67b8765b7c28d370edba0e3758b2329b60e0e61c4d8e78d" }, "ok_hand_tone1": { "category": "people", "moji": "👌🏻", + "description": "ok hand sign tone 1", "unicodeVersion": "8.0", "digest": "ef1508efcf483b09807554fe0e451c2948224f9deb85463e8e0dad6875b54012" }, "ok_hand_tone2": { "category": "people", "moji": "👌🏼", + "description": "ok hand sign tone 2", "unicodeVersion": "8.0", "digest": "1215a101a082fd8e04c5d2f7e3c59d0f480cb0bedd79aeab5d36676bfe760088" }, "ok_hand_tone3": { "category": "people", "moji": "👌🏽", + "description": "ok hand sign tone 3", "unicodeVersion": "8.0", "digest": "6fe0ed9fb42e86bb2bed4cb37b2acacacda1471fb1ee845ad55e54fb0897fbf4" }, "ok_hand_tone4": { "category": "people", "moji": "👌🏾", + "description": "ok hand sign tone 4", "unicodeVersion": "8.0", "digest": "bfb9041c49d95e901a667264abaf9b398f6c4aa8b52bf5191c122db20c13c020" }, "ok_hand_tone5": { "category": "people", "moji": "👌🏿", + "description": "ok hand sign tone 5", "unicodeVersion": "8.0", "digest": "1c218dc04d698da2cbdd7bea1ca3f845f9b386e967b7247c52f4b0f6ec8f5320" }, "ok_woman": { "category": "people", "moji": "🙆", + "description": "face with ok gesture", "unicodeVersion": "6.0", "digest": "3f8bd4ce2c4497155d697e5a71ebdc9339f65633d07fa9a7903e1bd76cfa4ba1" }, "ok_woman_tone1": { "category": "people", "moji": "🙆🏻", + "description": "face with ok gesture tone1", "unicodeVersion": "8.0", "digest": "1660cd904ccd2ecdc6f4ba00527f7d4ec8c33f3c6183344616f97badae4c3730" }, "ok_woman_tone2": { "category": "people", "moji": "🙆🏼", + "description": "face with ok gesture tone2", "unicodeVersion": "8.0", "digest": "7ba5fddd1e141424fac6778894dfc5af28e125839c58937c69496f99cd2c4002" }, "ok_woman_tone3": { "category": "people", "moji": "🙆🏽", + "description": "face with ok gesture tone3", "unicodeVersion": "8.0", "digest": "1d972b8377c52f598406f59ab1e5be41aaf8f027e1fefba3deda66312ccd6a9b" }, "ok_woman_tone4": { "category": "people", "moji": "🙆🏾", + "description": "face with ok gesture tone4", "unicodeVersion": "8.0", "digest": "a176328d8f53503aa743448968afd21d72ffd3510555526a3fb38d6b30ee7c15" }, "ok_woman_tone5": { "category": "people", "moji": "🙆🏿", + "description": "face with ok gesture tone5", "unicodeVersion": "8.0", "digest": "13cfc1b589c57e81f768ee07a14b737cafc71407a7eb0956728b2ec4b1df14c4" }, "older_man": { "category": "people", "moji": "👴", + "description": "older man", "unicodeVersion": "6.0", "digest": "4c0462b199bf26181c9e4d2d4cb878a32b0294566941212efc67362d0645f948" }, "older_man_tone1": { "category": "people", "moji": "👴🏻", + "description": "older man tone 1", "unicodeVersion": "8.0", "digest": "99baa083f78cb01166d0a928d0b53682be14be04c29fc17bef14aac1a73a61e6" }, "older_man_tone2": { "category": "people", "moji": "👴🏼", + "description": "older man tone 2", "unicodeVersion": "8.0", "digest": "5b4ce713e8820ba517fe92c25f3b93e6a6bf3704d1f982c461d5f31fc02b9d3d" }, "older_man_tone3": { "category": "people", "moji": "👴🏽", + "description": "older man tone 3", "unicodeVersion": "8.0", "digest": "0eff72b3226c3a703c635798ee84129a695c896fa011fe1adbc105312eecc083" }, "older_man_tone4": { "category": "people", "moji": "👴🏾", + "description": "older man tone 4", "unicodeVersion": "8.0", "digest": "ad9ba82b0c5d3b171b0639ee4265370dbddff5e0eeb70729db122659bb8c8f84" }, "older_man_tone5": { "category": "people", "moji": "👴🏿", + "description": "older man tone 5", "unicodeVersion": "8.0", "digest": "5eb0a7467cc40e75752e11fd5126b275863dc037557a0d0d3b24b681e00c2386" }, "older_woman": { "category": "people", "moji": "👵", + "description": "older woman", "unicodeVersion": "6.0", "digest": "c261fdf3b01e0c7d949e177144531add5895197fbadf1acbba8eb17d18766bf6" }, "older_woman_tone1": { "category": "people", "moji": "👵🏻", + "description": "older woman tone 1", "unicodeVersion": "8.0", "digest": "1f2bb9e42270a58194498254da27ac2b7a50edaa771b90ee194ccd6d24660c62" }, "older_woman_tone2": { "category": "people", "moji": "👵🏼", + "description": "older woman tone 2", "unicodeVersion": "8.0", "digest": "2e28198e9b7ac08c55980677ed66655fd899e157f14184958bebd87fcd714940" }, "older_woman_tone3": { "category": "people", "moji": "👵🏽", + "description": "older woman tone 3", "unicodeVersion": "8.0", "digest": "c968be0170f7e0c65d4f796337034cfb1daba897884da6fad85635ab5b6edf67" }, "older_woman_tone4": { "category": "people", "moji": "👵🏾", + "description": "older woman tone 4", "unicodeVersion": "8.0", "digest": "3596a6fa9a643bf79255afcd29657b03850df8499db9669b92ce013af908af44" }, "older_woman_tone5": { "category": "people", "moji": "👵🏿", + "description": "older woman tone 5", "unicodeVersion": "8.0", "digest": "c8998cb3dbd15e22bd1d6dad613d109ce371d9ffca3657e1a8afe5aeb30c1275" }, "om_symbol": { "category": "symbols", "moji": "🕉", + "description": "om symbol", "unicodeVersion": "7.0", "digest": "5ead73bea546ba9ba6da522f7280cc289c75ff5467742bdba31f92d0e1b3f4e6" }, "on": { "category": "symbols", "moji": "🔛", + "description": "on with exclamation mark with left right arrow abo", "unicodeVersion": "6.0", "digest": "9cc61a6b31a30c32dab594191bf23f91e341c4105384ab22158a6d43e6364631" }, "oncoming_automobile": { "category": "travel", "moji": "🚘", + "description": "oncoming automobile", "unicodeVersion": "6.0", "digest": "557c9cacdc3f95215d4f7a6f097a2baa7c007cb9c519492a6717077af4ca6b56" }, "oncoming_bus": { "category": "travel", "moji": "🚍", + "description": "oncoming bus", "unicodeVersion": "6.0", "digest": "059f28ce6bfb337e107db5982cbd2004844450ef20b4a54b9ca3cb738360ab05" }, "oncoming_police_car": { "category": "travel", "moji": "🚔", + "description": "oncoming police car", "unicodeVersion": "6.0", "digest": "aee79306a0d129cfc1980f58db80391eb46d2d7d5f814bf431414dc7680cab72" }, "oncoming_taxi": { "category": "travel", "moji": "🚖", + "description": "oncoming taxi", "unicodeVersion": "6.0", "digest": "84351489fc86d980b8d3eb9ec4e81120fe700b3ac01346daebe2b7aeb9607a55" }, "one": { "category": "symbols", "moji": "1️⃣", + "description": "keycap digit one", "unicodeVersion": "3.0", "digest": "d5d3fff04e68a114ff6464ee06fc831f3f381713045165f62a88d5e8215c195b" }, "open_file_folder": { "category": "objects", "moji": "📂", + "description": "open file folder", "unicodeVersion": "6.0", "digest": "96cfc322ee4903ae8cec07604811742245fd7d14f00bb70276d39d29c48bed28" }, "open_hands": { "category": "people", "moji": "👐", + "description": "open hands sign", "unicodeVersion": "6.0", "digest": "a6c131da2040b48103cea14f280e728675da50fa448d2b3f3438fcbb5bf5596a" }, "open_hands_tone1": { "category": "people", "moji": "👐🏻", + "description": "open hands sign tone 1", "unicodeVersion": "8.0", "digest": "867128dff2fa9b860c10c6b792f989f0c057928783696062378f834c0ef89d85" }, "open_hands_tone2": { "category": "people", "moji": "👐🏼", + "description": "open hands sign tone 2", "unicodeVersion": "8.0", "digest": "487ff2745b03d49bb3b1d0acd86ba530fd8cc3f467ca3fa504f88f0ef1cbbc01" }, "open_hands_tone3": { "category": "people", "moji": "👐🏽", + "description": "open hands sign tone 3", "unicodeVersion": "8.0", "digest": "cb8cddc8b8661f874ac9478289d16cc41406b947bb87f3363df518a588a53e16" }, "open_hands_tone4": { "category": "people", "moji": "👐🏾", + "description": "open hands sign tone 4", "unicodeVersion": "8.0", "digest": "17dcc2c07230846a769f3c79ce618a757c88b9b58c95c6c5b2d7f968814d447d" }, "open_hands_tone5": { "category": "people", "moji": "👐🏿", + "description": "open hands sign tone 5", "unicodeVersion": "8.0", "digest": "36b2493d67c84cea4f3f85a3088c6abcfd35cf99f7aeaeedfafa420ee878e3d2" }, "open_mouth": { "category": "people", "moji": "😮", + "description": "face with open mouth", "unicodeVersion": "6.1", "digest": "1906c5100ae0c8326ca5c4f9422976958a38dadd8d77724d68538a25d9623035" }, "ophiuchus": { "category": "symbols", "moji": "⛎", + "description": "ophiuchus", "unicodeVersion": "6.0", "digest": "6112e2a1656b1cb8bd9a8b0dfa6cbf66d30cae671710a9ef75c821de344aab2b" }, "orange_book": { "category": "objects", "moji": "📙", + "description": "orange book", "unicodeVersion": "6.0", "digest": "41141b08d2beceded21a94795431603c47fd7d42a3a472a2aa8b2bb25fa87ebf" }, "orthodox_cross": { "category": "symbols", "moji": "☦", + "description": "orthodox cross", "unicodeVersion": "1.1", "digest": "c16372102f0169dd6d32eb2b27a633aaee74e4e0fddcf723c15ad97f9dc6075c" }, "outbox_tray": { "category": "objects", "moji": "📤", + "description": "outbox tray", "unicodeVersion": "6.0", "digest": "e47cb481a0ffcb39996f32fd313e19b362a91d8dda15ffca48ac23a3b5bb5baf" }, "owl": { "category": "nature", "moji": "🦉", + "description": "owl", "unicodeVersion": "9.0", "digest": "f62ec1ad23ad9038966eea8d8b79660ac212f291af2e89bcdb0fdc683caf41e5" }, "ox": { "category": "nature", "moji": "🐂", + "description": "ox", "unicodeVersion": "6.0", "digest": "d13bc60552190bb9936bf32d681bdc742439b702a09cfc62137ea09a98624aed" }, "package": { "category": "objects", "moji": "📦", + "description": "package", "unicodeVersion": "6.0", "digest": "e82bf5accebb65136e897c15607eef635fb79fd7b2d8c8e19a9eb00b6786918c" }, "page_facing_up": { "category": "objects", "moji": "📄", + "description": "page facing up", "unicodeVersion": "6.0", "digest": "3884868bdcb2f29615b09a13a30385cbc5269379094a54b5a7e8a5f4e8ce905a" }, "page_with_curl": { "category": "objects", "moji": "📃", + "description": "page with curl", "unicodeVersion": "6.0", "digest": "3d6257670189f841ad1fa45415c34feb2433b2cb35bb435c4ee122ce89b39669" }, "pager": { "category": "objects", "moji": "📟", + "description": "pager", "unicodeVersion": "6.0", "digest": "e21c756cc1c58ebc1b37ebcd38e22a25b31e2e81306c6f18285d6a7671f9eb12" }, "paintbrush": { "category": "objects", "moji": "🖌", + "description": "lower left paintbrush", "unicodeVersion": "7.0", "digest": "fc0da7a25b726b8be9dd6467953e27293d2313a21eeff21424c2a19be614fff2" }, "palm_tree": { "category": "nature", "moji": "🌴", + "description": "palm tree", "unicodeVersion": "6.0", "digest": "90fedafd62fe0abf51325174d0f293ebb9a4794913b9ba93b12f2d0119056df1" }, "pancakes": { "category": "food", "moji": "🥞", + "description": "pancakes", "unicodeVersion": "9.0", "digest": "5256b4832431e8a88555796b1a9726f12d909a26fb2bdc3a0abff76412c45903" }, "panda_face": { "category": "nature", "moji": "🐼", + "description": "panda face", "unicodeVersion": "6.0", "digest": "56a4b84abe983bd6569be1b81ac5e43071015fd308389a16b92231310ae56a5b" }, "paperclip": { "category": "objects", "moji": "📎", + "description": "paperclip", "unicodeVersion": "6.0", "digest": "d1e2ce94a12b7e8b7a9bba49e47ddc7432ec0288545d3b6817c7a499e806e3f0" }, "paperclips": { "category": "objects", "moji": "🖇", + "description": "linked paperclips", "unicodeVersion": "7.0", "digest": "70cefa0d0777f070e393e9f95c24146fe2dd627f30fa3845baa19310d9291fe2" }, "park": { "category": "travel", "moji": "🏞", + "description": "national park", "unicodeVersion": "7.0", "digest": "444dce8014e0817ddd756c36a38adfbbf7ae4c6aa509e4cae291828f0716d5e7" }, "parking": { "category": "symbols", "moji": "🅿", + "description": "negative squared latin capital letter p", "unicodeVersion": "5.2", "digest": "9f1da460a7dd58b26beab8cf701be2691fb812208fbc941c71daa35be1507c2f" }, "part_alternation_mark": { "category": "symbols", "moji": "〽", + "description": "part alternation mark", "unicodeVersion": "3.2", "digest": "956da19353bb38fd4dfe0ab5360679a9035d566858fb5de62887b85c75fb8eef" }, "partly_sunny": { "category": "nature", "moji": "⛅", + "description": "sun behind cloud", "unicodeVersion": "5.2", "digest": "8fb9a6d2caf9e0cce58447762f0dfd6aa0b581b2e83fea6411348e0cbc8cf3c4" }, "passport_control": { "category": "symbols", "moji": "🛂", + "description": "passport control", "unicodeVersion": "6.0", "digest": "d9be6eed2c90e1c89171c42d70a06485fdf86a4c68833371832cc1f6897fadd0" }, "pause_button": { "category": "symbols", "moji": "⏸", + "description": "double vertical bar", "unicodeVersion": "7.0", "digest": "143221d99e82399ed7824b6c5e185700896492058b65c04e4c668291de78b203" }, "peace": { "category": "symbols", "moji": "☮", + "description": "peace symbol", "unicodeVersion": "1.1", "digest": "65181429e373c1f0507bbd98425c1bec0c042d648fb285a392460cbce60f44d4" }, "peach": { "category": "food", "moji": "🍑", + "description": "peach", "unicodeVersion": "6.0", "digest": "768d1f4f29e1e06aff5abb29043be83087ded16427ce6a2d0f682814e665e311" }, "peanuts": { "category": "food", "moji": "🥜", + "description": "peanuts", "unicodeVersion": "9.0", "digest": "e2384846b6e4a6c3a56e991ebb749cb68b330ac00a9e9d888b2c39105ff7ff5d" }, "pear": { "category": "food", "moji": "🍐", + "description": "pear", "unicodeVersion": "6.0", "digest": "b7c9cf90bb979649b863d2f4132f1b51f6f8107d42e08fb8b4033fea32844948" }, "pen_ballpoint": { "category": "objects", "moji": "🖊", + "description": "lower left ballpoint pen", "unicodeVersion": "7.0", "digest": "aacb20b220f26704e10303deeea33be0eec2d3811dcba7795902ca44b6ae9876" }, "pen_fountain": { "category": "objects", "moji": "🖋", + "description": "lower left fountain pen", "unicodeVersion": "7.0", "digest": "3619913eab2b6291f518b40481bb3eca0820d68b0a1b3c11fb6a69c62b75a626" }, "pencil": { "category": "objects", "moji": "📝", + "description": "memo", "unicodeVersion": "6.0", "digest": "accbc3f1439b7faa4411e502385f78a16c8e71851f71fc13582753291ffb507c" }, "pencil2": { "category": "objects", "moji": "✏", + "description": "pencil", "unicodeVersion": "1.1", "digest": "9ca1b56b5726f472b1f1b23050ed163e213916dac379d22e38e4c8358fe871e0" }, "penguin": { "category": "nature", "moji": "🐧", + "description": "penguin", "unicodeVersion": "6.0", "digest": "a1800ab931d6dc84a9c89bfab2c815198025c276d952509c55b18dd20bd9d316" }, "pensive": { "category": "people", "moji": "😔", + "description": "pensive face", "unicodeVersion": "6.0", "digest": "d237deff9f5ead8a0b281b7e5c6f4b82e98cc30c80c86c22c3fdc6160090b2f2" }, "performing_arts": { "category": "activity", "moji": "🎭", + "description": "performing arts", "unicodeVersion": "6.0", "digest": "d7c7bc9213e308ca26286cbbd8012e656b0f9b00293758faf1bfccc4c5ceabed" }, "persevere": { "category": "people", "moji": "😣", + "description": "persevering face", "unicodeVersion": "6.0", "digest": "c361509c9b8663af19a02a1ffff61b1b0d0b4bd75d693ce3d406b0ca1bde1ca0" }, "person_frowning": { "category": "people", "moji": "🙍", + "description": "person frowning", "unicodeVersion": "6.0", "digest": "b37be8bd95f21a6860ad3f171b8086125ab37331b382d87bcdb4cd684800546b" }, "person_frowning_tone1": { "category": "people", "moji": "🙍🏻", + "description": "person frowning tone 1", "unicodeVersion": "8.0", "digest": "3d5e78a367f9673baed2a86bc11cf04fd44394aadb65291fa51ade8dca318427" }, "person_frowning_tone2": { "category": "people", "moji": "🙍🏼", + "description": "person frowning tone 2", "unicodeVersion": "8.0", "digest": "7456c414c65ad6b6f11855f68a2eedc18113526f86862c4373202397cb1bed2c" }, "person_frowning_tone3": { "category": "people", "moji": "🙍🏽", + "description": "person frowning tone 3", "unicodeVersion": "8.0", "digest": "c86cf2d6951f1e6a7c786a74caaf68a777cf00e88023e23849d4383f864ae437" }, "person_frowning_tone4": { "category": "people", "moji": "🙍🏾", + "description": "person frowning tone 4", "unicodeVersion": "8.0", "digest": "944e96ced645ced8db6bb50120c7e37ed46b6960d595cbfe964c81803efa83aa" }, "person_frowning_tone5": { "category": "people", "moji": "🙍🏿", + "description": "person frowning tone 5", "unicodeVersion": "8.0", "digest": "4bd0ea571be6ef9f0493784ef0d12d5e47bc2d6ac610fb42c450bf3d87fb2948" }, "person_with_blond_hair": { "category": "people", "moji": "👱", + "description": "person with blond hair", "unicodeVersion": "6.0", "digest": "a7f94ede2e43308108c2260d83fc10121dda09a67f94a0a840e6d7bba7fd5616" }, "person_with_blond_hair_tone1": { "category": "people", "moji": "👱🏻", + "description": "person with blond hair tone 1", "unicodeVersion": "8.0", "digest": "00a116357a7878554c83e5bade4bddfa9cfabf76a229efa19cbb58e0d216219c" }, "person_with_blond_hair_tone2": { "category": "people", "moji": "👱🏼", + "description": "person with blond hair tone 2", "unicodeVersion": "8.0", "digest": "df509ebe92ed3138b9d5bd4645eff4b13f77f714cf62bb949c59eff1adc00019" }, "person_with_blond_hair_tone3": { "category": "people", "moji": "👱🏽", + "description": "person with blond hair tone 3", "unicodeVersion": "8.0", "digest": "6f328513f440a0c8cd1dc44596a5028fd8f306bdaf57c1e6f3aa94a3aa262b3c" }, "person_with_blond_hair_tone4": { "category": "people", "moji": "👱🏾", + "description": "person with blond hair tone 4", "unicodeVersion": "8.0", "digest": "32df1a577815b009696643ad80d063cc97b35d54add6d4e5517fc936f6da9ee8" }, "person_with_blond_hair_tone5": { "category": "people", "moji": "👱🏿", + "description": "person with blond hair tone 5", "unicodeVersion": "8.0", "digest": "2e270bb39187d8e36a33f4aa4d6045308189595fafc157cf7993e82d7ce93442" }, "person_with_pouting_face": { "category": "people", "moji": "🙎", + "description": "person with pouting face", "unicodeVersion": "6.0", "digest": "57e9a6e5f82121516dc189173f2a63b218f726cd51014e24a18c2bdfeeec3a0b" }, "person_with_pouting_face_tone1": { "category": "people", "moji": "🙎🏻", + "description": "person with pouting face tone1", "unicodeVersion": "8.0", "digest": "d10dadb1ac03fc2e221eff77b4c47935dc0b4fe897af3de30461e7226c3b4bbc" }, "person_with_pouting_face_tone2": { "category": "people", "moji": "🙎🏼", + "description": "person with pouting face tone2", "unicodeVersion": "8.0", "digest": "efface531537ab934b3b96985210a2dac88de812e82e804d6ec12174e536d1cc" }, "person_with_pouting_face_tone3": { "category": "people", "moji": "🙎🏽", + "description": "person with pouting face tone3", "unicodeVersion": "8.0", "digest": "7ff26ece237216b949bfa96d16bd12cfd248c6fd3e4ed89aa6c735c09eafaeff" }, "person_with_pouting_face_tone4": { "category": "people", "moji": "🙎🏾", + "description": "person with pouting face tone4", "unicodeVersion": "8.0", "digest": "045c04105df41d94ff4942133c7394e42ff35ef76c4ccb711497ab77ae6219f2" }, "person_with_pouting_face_tone5": { "category": "people", "moji": "🙎🏿", + "description": "person with pouting face tone5", "unicodeVersion": "8.0", "digest": "783ee37f146fcf61d38af5009f5823cf6526fe99ed891979f454016bce9dd4ba" }, "pick": { "category": "objects", "moji": "⛏", + "description": "pick", "unicodeVersion": "5.2", "digest": "7f0ec5445b4d5c66cf46e2a7332946cce34bd70e9929ac7a119251a7f57f555d" }, "pig": { "category": "nature", "moji": "🐷", + "description": "pig face", "unicodeVersion": "6.0", "digest": "51362570ab36805c8f67622ee4543e38811f8abb20f732a1af2ffbff2d63d042" }, "pig2": { "category": "nature", "moji": "🐖", + "description": "pig", "unicodeVersion": "6.0", "digest": "67010e255f28061b9d9210bcdab6edc072642ad134122a1d0c7e3a6b1795a45b" }, "pig_nose": { "category": "nature", "moji": "🐽", + "description": "pig nose", "unicodeVersion": "6.0", "digest": "0b21cac238bf4910939fbea9bed35552378c1b605a3867d7b85c1556dbda22a9" }, "pill": { "category": "objects", "moji": "💊", + "description": "pill", "unicodeVersion": "6.0", "digest": "cb00be361aaba6dbcf8da58bd20b76221dd75031362ecae99496b088ed413a7f" }, "pineapple": { "category": "food", "moji": "🍍", + "description": "pineapple", "unicodeVersion": "6.0", "digest": "621d4d4c52b59e566c2e29ed7845c8bd2d1da0946577527342097808d170dd70" }, "ping_pong": { "category": "activity", "moji": "🏓", + "description": "table tennis paddle and ball", "unicodeVersion": "8.0", "digest": "943a858bd054c81a08a08951f8351c27c8009b85a9359729c7362868298b58e1" }, "pisces": { "category": "symbols", "moji": "♓", + "description": "pisces", "unicodeVersion": "1.1", "digest": "453c3915122a4b6b32867056d2447be48675a84469145c88d52f8007fcb0861a" }, "pizza": { "category": "food", "moji": "🍕", + "description": "slice of pizza", "unicodeVersion": "6.0", "digest": "169bc6c1e1d7fdab1b8bf2eab0eeec4f9a7ae08b7b9b38f33b0b0c642e72053a" }, "place_of_worship": { "category": "symbols", "moji": "🛐", + "description": "place of worship", "unicodeVersion": "8.0", "digest": "daf271d36a38ee8c0f8b9de84c128ab8b25a5b7df8f107308d0353c961f2c644" }, "play_pause": { "category": "symbols", "moji": "⏯", + "description": "black right-pointing double triangle with double vertical bar", "unicodeVersion": "6.0", "digest": "af1498f34a3d6e0da8bbd26ebaa447e697e2df08c8eb255437cf7905c93f8c42" }, "point_down": { "category": "people", "moji": "👇", + "description": "white down pointing backhand index", "unicodeVersion": "6.0", "digest": "4ecdb3f31c16dc38113b8854ec1a7884613b688a185ebdf967eab9a81018f76d" }, "point_down_tone1": { "category": "people", "moji": "👇🏻", + "description": "white down pointing backhand index tone 1", "unicodeVersion": "8.0", "digest": "c74a7c94367cddbfa840542dc0924adeb0d108be0c7fde8c25fb95d69115d283" }, "point_down_tone2": { "category": "people", "moji": "👇🏼", + "description": "white down pointing backhand index tone 2", "unicodeVersion": "8.0", "digest": "dc4bda0726d85418b974addb42738f437fbb9cf16e5815cdbab3859c4ada6cae" }, "point_down_tone3": { "category": "people", "moji": "👇🏽", + "description": "white down pointing backhand index tone 3", "unicodeVersion": "8.0", "digest": "e460f81a501376d2f0ed1d45e358c5ed03ba049e8f466e4298afb4f3ca6d24dc" }, "point_down_tone4": { "category": "people", "moji": "👇🏾", + "description": "white down pointing backhand index tone 4", "unicodeVersion": "8.0", "digest": "4bc91cd771f24e0f897a9d8b18f323fec9a82da0fc2429c4a7e4e6a9d885a0a3" }, "point_down_tone5": { "category": "people", "moji": "👇🏿", + "description": "white down pointing backhand index tone 5", "unicodeVersion": "8.0", "digest": "7e47c6bc73250f36dc7ae1c1c09e7b41f30647b9d0ff703a53a75cc046b5057d" }, "point_left": { "category": "people", "moji": "👈", + "description": "white left pointing backhand index", "unicodeVersion": "6.0", "digest": "b5a7e864a0016afbadb3bec41f51ecf8c4af73cc20462e1a08b357f90bca6879" }, "point_left_tone1": { "category": "people", "moji": "👈🏻", + "description": "white left pointing backhand index tone 1", "unicodeVersion": "8.0", "digest": "9f1868272a10a2b738c065be5d30241643324550cfd47baf01c7a09060e66d31" }, "point_left_tone2": { "category": "people", "moji": "👈🏼", + "description": "white left pointing backhand index tone 2", "unicodeVersion": "8.0", "digest": "bf0d58c68178a2c2c01d4a6235a1a66b90073cea170f9f6fe2668b6dd68424f7" }, "point_left_tone3": { "category": "people", "moji": "👈🏽", + "description": "white left pointing backhand index tone 3", "unicodeVersion": "8.0", "digest": "34d28c97bc8f9d111d14e328153c4298fc32cf18e39e20aacaec17846645ed90" }, "point_left_tone4": { "category": "people", "moji": "👈🏾", + "description": "white left pointing backhand index tone 4", "unicodeVersion": "8.0", "digest": "c40c8436316915d516c53bb1c98a469528cefd98baa719be7e748c4608cbbcc9" }, "point_left_tone5": { "category": "people", "moji": "👈🏿", + "description": "white left pointing backhand index tone 5", "unicodeVersion": "8.0", "digest": "c410fe32e4ce0ded74845a54b86090e59e5820d457837b16e175b36cc71ecb46" }, "point_right": { "category": "people", "moji": "👉", + "description": "white right pointing backhand index", "unicodeVersion": "6.0", "digest": "44d9251ab41f2f48c2250c44a47f92b3476a71f13fbbbfb637547db837fd5a49" }, "point_right_tone1": { "category": "people", "moji": "👉🏻", + "description": "white right pointing backhand index tone 1", "unicodeVersion": "8.0", "digest": "9fcce259eb81c0b52ec7796b98a1653194e3a9021a1d338df1dbbab7522fc406" }, "point_right_tone2": { "category": "people", "moji": "👉🏼", + "description": "white right pointing backhand index tone 2", "unicodeVersion": "8.0", "digest": "9d00a0b1cfc435674dc56065b3d28d28839196977504cf20581205351d8708f2" }, "point_right_tone3": { "category": "people", "moji": "👉🏽", + "description": "white right pointing backhand index tone 3", "unicodeVersion": "8.0", "digest": "e3026a70630ba73d76892a055a80cac2f78d509faddce737f802d2abefa074ba" }, "point_right_tone4": { "category": "people", "moji": "👉🏾", + "description": "white right pointing backhand index tone 4", "unicodeVersion": "8.0", "digest": "ea508fde90561460361773b4e1b8e80874667b19ac115926206e7c592587cb76" }, "point_right_tone5": { "category": "people", "moji": "👉🏿", + "description": "white right pointing backhand index tone 5", "unicodeVersion": "8.0", "digest": "d59cdb2864eb2929941ecd233f8b8afcddc30fbd4594e5f9acf6386ae06ac12c" }, "point_up": { "category": "people", "moji": "☝", + "description": "white up pointing index", "unicodeVersion": "1.1", "digest": "b69ff4f650989709f2185822d278c7773672bd9eb4a625da80f3038a2b9ce42b" }, "point_up_2": { "category": "people", "moji": "👆", + "description": "white up pointing backhand index", "unicodeVersion": "6.0", "digest": "e83cd9eff2af5125a25f5a306c3ee3cfea240add683b5c36a86a994a8d8c805c" }, "point_up_2_tone1": { "category": "people", "moji": "👆🏻", + "description": "white up pointing backhand index tone 1", "unicodeVersion": "8.0", "digest": "b02ec3e7e04a83bfb769cffb951cbf32aa78e56fa5a51c097f9326df9e08ed33" }, "point_up_2_tone2": { "category": "people", "moji": "👆🏼", + "description": "white up pointing backhand index tone 2", "unicodeVersion": "8.0", "digest": "32994b85c8b4a1383ca985ebc3382be88866cea1ff1315adfb71fb05e992a232" }, "point_up_2_tone3": { "category": "people", "moji": "👆🏽", + "description": "white up pointing backhand index tone 3", "unicodeVersion": "8.0", "digest": "9e263bcfb82ada34ff85291f36e64e66b86760fb11a4e0c554e801644d417d6d" }, "point_up_2_tone4": { "category": "people", "moji": "👆🏾", + "description": "white up pointing backhand index tone 4", "unicodeVersion": "8.0", "digest": "3edc92130a0851ac7b5236772ce7918d088689221df287098688e1ed5b3ff181" }, "point_up_2_tone5": { "category": "people", "moji": "👆🏿", + "description": "white up pointing backhand index tone 5", "unicodeVersion": "8.0", "digest": "cabb3b7da9290840ef59d0c8b22625bdb2e94842f01b0a575ccbc348f3069d77" }, "point_up_tone1": { "category": "people", "moji": "☝🏻", + "description": "white up pointing index tone 1", "unicodeVersion": "8.0", "digest": "e496fda349072f8b321ceb7a251175f7244c3076661f5ede48ea75ba1acf8339" }, "point_up_tone2": { "category": "people", "moji": "☝🏼", + "description": "white up pointing index tone 2", "unicodeVersion": "8.0", "digest": "5a8081323f3baa67e6431e21e16a36559b339f5175d586644e34947f738dd07a" }, "point_up_tone3": { "category": "people", "moji": "☝🏽", + "description": "white up pointing index tone 3", "unicodeVersion": "8.0", "digest": "07bf0cea812eb226b443334e026e13d1ec23e013478f4af862a3919703107842" }, "point_up_tone4": { "category": "people", "moji": "☝🏾", + "description": "white up pointing index tone 4", "unicodeVersion": "8.0", "digest": "1fbbd71433108143ee157d0fdadd183f7f013bafa96f0dd93b181e1fd5fd4af2" }, "point_up_tone5": { "category": "people", "moji": "☝🏿", + "description": "white up pointing index tone 5", "unicodeVersion": "8.0", "digest": "ad068ef32df32f8297955490a9a90590a0f93ed5702a052cd0d8f6484c6cc679" }, "police_car": { "category": "travel", "moji": "🚓", + "description": "police car", "unicodeVersion": "6.0", "digest": "0909be1bd615ae331a7cce71e16dee3ca663c721d5170072c593cb7c76f9f661" }, "poodle": { "category": "nature", "moji": "🐩", + "description": "poodle", "unicodeVersion": "6.0", "digest": "f1742fdf3fd26a8a5cfeaba57026518dacaad364cbd03344c4000a35af13e47a" }, "poop": { "category": "people", "moji": "💩", + "description": "pile of poo", "unicodeVersion": "6.0", "digest": "857a61c872138d359a7fe8257bb26118afa49d75186eca2addb415d07c92b3ec" }, "popcorn": { "category": "food", "moji": "🍿", + "description": "popcorn", "unicodeVersion": "8.0", "digest": "684f1b7ef34ea7ca933aed41569bc6595a19ef0d546a1b7b9e69f8335540b323" }, "post_office": { "category": "travel", "moji": "🏣", + "description": "japanese post office", "unicodeVersion": "6.0", "digest": "54398ee396c1314a7993b1cb1cba264946b5c9d5a7dbb43fd67286854d1d1a0f" }, "postal_horn": { "category": "objects", "moji": "📯", + "description": "postal horn", "unicodeVersion": "6.0", "digest": "0ea12f44f3bae9a14bde3b37361b48bd738d2f613bb1b53a9204959b70e643f8" }, "postbox": { "category": "objects", "moji": "📮", + "description": "postbox", "unicodeVersion": "6.0", "digest": "bbc424ae8d46de380d7023a43ea064002fd614657d00330d3503275827ac87e2" }, "potable_water": { "category": "symbols", "moji": "🚰", + "description": "potable water symbol", "unicodeVersion": "6.0", "digest": "dbe80d9637837377cc2a290da2e895f81a3108cc18b049e3d87212402c1c2098" }, "potato": { "category": "food", "moji": "🥔", + "description": "potato", "unicodeVersion": "9.0", "digest": "a56a69f36f3a0793f278726d92c0cea2960554f3062ef1a0904526a04511d8e1" }, "pouch": { "category": "people", "moji": "👝", + "description": "pouch", "unicodeVersion": "6.0", "digest": "9f012b90310b4a072b6a8fa2c64def087b5f7ffffaafc36e1856ba943a170351" }, "poultry_leg": { "category": "food", "moji": "🍗", + "description": "poultry leg", "unicodeVersion": "6.0", "digest": "1445ec4f5e68a19e5a84e5537dca8190d62409070c954d112e6097f1a6b7f054" }, "pound": { "category": "objects", "moji": "💷", + "description": "banknote with pound sign", "unicodeVersion": "6.0", "digest": "eb11b83eb52adb0a15e69a3bc15788a2dc7825dedee81ac3af84963c9dd517b5" }, "pouting_cat": { "category": "people", "moji": "😾", + "description": "pouting cat face", "unicodeVersion": "6.0", "digest": "8822abedf3499cf98278d7eeea0764d1100ec25cad71b4b2e877f9346f8c8138" }, "pray": { "category": "people", "moji": "🙏", + "description": "person with folded hands", "unicodeVersion": "6.0", "digest": "735b79dab34ac2cf81fd42fdcd7eb1f13c24655e5e343816d5764896c03edeea" }, "pray_tone1": { "category": "people", "moji": "🙏🏻", + "description": "person with folded hands tone 1", "unicodeVersion": "8.0", "digest": "e8b6103450215e8566797f150978355e297deade4eb47a6371f7a7bc558fed9d" }, "pray_tone2": { "category": "people", "moji": "🙏🏼", + "description": "person with folded hands tone 2", "unicodeVersion": "8.0", "digest": "ee8baacd95d7e8dbad8a1f2d9a12e36c98f3d518db5d3b117d0a18290815e62b" }, "pray_tone3": { "category": "people", "moji": "🙏🏽", + "description": "person with folded hands tone 3", "unicodeVersion": "8.0", "digest": "ae8c0caa9aca0a6c44069e76a7535c961d0284cd701812f76bbd2bd79ce2bd53" }, "pray_tone4": { "category": "people", "moji": "🙏🏾", + "description": "person with folded hands tone 4", "unicodeVersion": "8.0", "digest": "64f7b3178b8cd6f6a877ed583539eefe068fa87a0dd658fdcd58c8bc809f7e17" }, "pray_tone5": { "category": "people", "moji": "🙏🏿", + "description": "person with folded hands tone 5", "unicodeVersion": "8.0", "digest": "5bc8cdce937ac06779c87021423efcec4f602aa4a39dba90b00de81033005332" }, "prayer_beads": { "category": "objects", "moji": "📿", + "description": "prayer beads", "unicodeVersion": "8.0", "digest": "80177091264430cbcf7c994fbe5ee17319d1a58d933636cc752a54dafcf98a05" }, "pregnant_woman": { "category": "people", "moji": "🤰", + "description": "pregnant woman", "unicodeVersion": "9.0", "digest": "49abb86409103338bdb6ae43c13a78ca2dc9cd158a26df35eadd0da3c84a4352" }, "pregnant_woman_tone1": { "category": "people", "moji": "🤰🏻", + "description": "pregnant woman tone 1", "unicodeVersion": "9.0", "digest": "5a9f8ed2b631ecf8af111803a5c11f4c156435a5293cb50329c7b98697c8da25" }, "pregnant_woman_tone2": { "category": "people", "moji": "🤰🏼", + "description": "pregnant woman tone 2", "unicodeVersion": "9.0", "digest": "279a2eafff603b11629c955b05f5bd3d7da9a271d4fb3f02e9ccd457b8d2d815" }, "pregnant_woman_tone3": { "category": "people", "moji": "🤰🏽", + "description": "pregnant woman tone 3", "unicodeVersion": "9.0", "digest": "93bb63ec2312db315e3f0065520b715cc413ac0fd65538ec9b5cd97df2a42b20" }, "pregnant_woman_tone4": { "category": "people", "moji": "🤰🏾", + "description": "pregnant woman tone 4", "unicodeVersion": "9.0", "digest": "b8dc3dcec894bfd832a249459b10850f8786b6778d8887a677d1291865623da2" }, "pregnant_woman_tone5": { "category": "people", "moji": "🤰🏿", + "description": "pregnant woman tone 5", "unicodeVersion": "9.0", "digest": "73ee432752f81980f353a7f9b9f7a5ece62512dca08e15c1876b89227face21c" }, "prince": { "category": "people", "moji": "🤴", + "description": "prince", "unicodeVersion": "9.0", "digest": "34a0e0625f0a9825d3674192d6233b6cae4d8130451293df09f91a6a4165869c" }, "prince_tone1": { "category": "people", "moji": "🤴🏻", + "description": "prince tone 1", "unicodeVersion": "9.0", "digest": "ccecdfeccb2ab1fceceae14f3fba875c8c7099785a4c40131c08a697b5b675fc" }, "prince_tone2": { "category": "people", "moji": "🤴🏼", + "description": "prince tone 2", "unicodeVersion": "9.0", "digest": "c373fd3e0c1798415e3d8d88fab6c98c1bbdedcbe6f52f3a3899f6e2124a768d" }, "prince_tone3": { "category": "people", "moji": "🤴🏽", + "description": "prince tone 3", "unicodeVersion": "9.0", "digest": "71d15695ca954d55aa69d3c753c7d31a8ba5329713a8ddbc90dafc11e524c4ef" }, "prince_tone4": { "category": "people", "moji": "🤴🏾", + "description": "prince tone 4", "unicodeVersion": "9.0", "digest": "08f6cb32424f15cc3aaf83c31a5dac7c01a6be2f37ea8f13aed579ce6fb4db19" }, "prince_tone5": { "category": "people", "moji": "🤴🏿", + "description": "prince tone 5", "unicodeVersion": "9.0", "digest": "77d521148efa33fa4d3409693d050fecfd948411e807327484f174e289834649" }, "princess": { "category": "people", "moji": "👸", + "description": "princess", "unicodeVersion": "6.0", "digest": "efabd28480a843c735f0868734da2f9ce28133933b02ab07b645498f494f3f80" }, "princess_tone1": { "category": "people", "moji": "👸🏻", + "description": "princess tone 1", "unicodeVersion": "8.0", "digest": "52b88b99ba64f82e8f36e2a1827c85145e4fcd6863478c2345fe9fa9e8901cdf" }, "princess_tone2": { "category": "people", "moji": "👸🏼", + "description": "princess tone 2", "unicodeVersion": "8.0", "digest": "7e44289404693668f20e681fcdc2e516512d54a69c627eedae958f69dfe6eea9" }, "princess_tone3": { "category": "people", "moji": "👸🏽", + "description": "princess tone 3", "unicodeVersion": "8.0", "digest": "96c9a9857348d7a1a8be899c50d55b352b9a9fd5c65e4777bfa199fe7929d41c" }, "princess_tone4": { "category": "people", "moji": "👸🏾", + "description": "princess tone 4", "unicodeVersion": "8.0", "digest": "67696f96be60f2a36598072172d2db197d007e6c1ac3acef526a5ce6d59bf3f7" }, "princess_tone5": { "category": "people", "moji": "👸🏿", + "description": "princess tone 5", "unicodeVersion": "8.0", "digest": "007f624e2fad91bb57ce32ecd35213a796d71807f3b12f3f1575bf50e6a50eeb" }, "printer": { "category": "objects", "moji": "🖨", + "description": "printer", "unicodeVersion": "7.0", "digest": "5e5307e3dc7ec4e16c9978fb00934c99c4adefca7d32732a244d1f2de71ce6f8" }, "projector": { "category": "objects", "moji": "📽", + "description": "film projector", "unicodeVersion": "7.0", "digest": "7f8e1fdb89584849a56ee34c62cab808af48b7bd4823467d090af4657a2e0420" }, "punch": { "category": "people", "moji": "👊", + "description": "fisted hand sign", "unicodeVersion": "6.0", "digest": "c7e7edf6d64f755db3f02874354f08337b3971aff329476d19ac946e0b421329" }, "punch_tone1": { "category": "people", "moji": "👊🏻", + "description": "fisted hand sign tone 1", "unicodeVersion": "8.0", "digest": "c9ba508b0c36041047473782acfedab5af40dd7946b33daf4d8d54c726e06a11" }, "punch_tone2": { "category": "people", "moji": "👊🏼", + "description": "fisted hand sign tone 2", "unicodeVersion": "8.0", "digest": "d53011cd2f3334c7b3fffdfe1e2b8cc1c832c74306e1ac6d03f954a1309d7d0b" }, "punch_tone3": { "category": "people", "moji": "👊🏽", + "description": "fisted hand sign tone 3", "unicodeVersion": "8.0", "digest": "f7522347094e0130ed8e304678106574dbd7dd2b6b3aeb4d8a7a0fef880920b2" }, "punch_tone4": { "category": "people", "moji": "👊🏾", + "description": "fisted hand sign tone 4", "unicodeVersion": "8.0", "digest": "3e62bdd426f3e6ff175ce3b8dd6f6d3998d9c1506128defa96b528b455295b47" }, "punch_tone5": { "category": "people", "moji": "👊🏿", + "description": "fisted hand sign tone 5", "unicodeVersion": "8.0", "digest": "7d9bff777dc4ec41ac132b1252fa08cf92a398c8dc146c4a5327b45d568982d8" }, "purple_heart": { "category": "symbols", "moji": "💜", + "description": "purple heart", "unicodeVersion": "6.0", "digest": "a6bf01de806525942be480e45a4b2879f91df8129b78a1b8734d4f917bcab773" }, "purse": { "category": "people", "moji": "👛", + "description": "purse", "unicodeVersion": "6.0", "digest": "2b785f36e01875d66cfda2192c8c53606e7224a7c869a4826b62cb61613d60c8" }, "pushpin": { "category": "objects", "moji": "📌", + "description": "pushpin", "unicodeVersion": "6.0", "digest": "c3f7d7008be6bab8dc02284d4d759abf7aafbb3dbbe3a53f0f5b2ff685af88f8" }, "put_litter_in_its_place": { "category": "symbols", "moji": "🚮", + "description": "put litter in its place symbol", "unicodeVersion": "6.0", "digest": "f52a57d6f1bada7b6e6b9a6458597d70cb701c01e1120d8cb1d7ff65e01d405c" }, "question": { "category": "symbols", "moji": "❓", + "description": "black question mark ornament", "unicodeVersion": "6.0", "digest": "40050a1fd29bed321fd601d13dc33de5d6084121f1d873b29bde9dc3d823a310" }, "rabbit": { "category": "nature", "moji": "🐰", + "description": "rabbit face", "unicodeVersion": "6.0", "digest": "678ad953a7ab8f618c59051449a67c965d1f04f42dd6f6669adaf3fadebd080c" }, "rabbit2": { "category": "nature", "moji": "🐇", + "description": "rabbit", "unicodeVersion": "6.0", "digest": "19b1f5108292472434cc7a49efac4ea9275779735c7aeb0f15c36021d5998ca0" }, "race_car": { "category": "travel", "moji": "🏎", + "description": "racing car", "unicodeVersion": "7.0", "digest": "46f4814259d3d17ff35c04110e73e5327aee99f4711cd459ca1ee951508da3a6" }, "racehorse": { "category": "nature", "moji": "🐎", + "description": "horse", "unicodeVersion": "6.0", "digest": "a57b7aca35347ada8225eeee06b70cfd040484104963b4df56ea8fec690576b0" }, "radio": { "category": "objects", "moji": "📻", + "description": "radio", "unicodeVersion": "6.0", "digest": "9245951dd779cdd141089891b15a90d3999a6358acf1fc296aa505100f812108" }, "radio_button": { "category": "symbols", "moji": "🔘", + "description": "radio button", "unicodeVersion": "6.0", "digest": "565bec59198df2592e96564c6e314d3cde33c47b453db1bec6c5d027b5cb4fd9" }, "radioactive": { "category": "symbols", "moji": "☢", + "description": "radioactive sign", "unicodeVersion": "1.1", "digest": "0ed6634057824e0cfd10b2533753e3632b0624341a7eac8d9835706480335581" }, "rage": { "category": "people", "moji": "😡", + "description": "pouting face", "unicodeVersion": "6.0", "digest": "d97ba6bd08eec46dbc7199f530c945b73a87a878e35397b0a3e4f2b45039e89e" }, "railway_car": { "category": "travel", "moji": "🚃", + "description": "railway car", "unicodeVersion": "6.0", "digest": "2cddc08d555e7fc24e312c3d255ed013fbf9cd2974a6918369c32554049ba2be" }, "railway_track": { "category": "travel", "moji": "🛤", + "description": "railway track", "unicodeVersion": "7.0", "digest": "0da351b6d4e75c6beeaef1225e151d9580d4b5c41dfa1cf192715bf3cec981d7" }, "rainbow": { "category": "travel", "moji": "🌈", + "description": "rainbow", "unicodeVersion": "6.0", "digest": "a93aceb54e965f35e397e8c8716b1831614933308d026012d5464ee42783ed4d" }, "raised_back_of_hand": { "category": "people", "moji": "🤚", + "description": "raised back of hand", "unicodeVersion": "9.0", "digest": "20973a697e826625deba5ee3c4f25eb5e1737f2e860ac6fe4ee4d0e0c84b5e12" }, "raised_back_of_hand_tone1": { "category": "people", "moji": "🤚🏻", + "description": "raised back of hand tone 1", "unicodeVersion": "9.0", "digest": "06af5941255ca69d10d99d0a512bbda6141a296453835dbccf259ce0afe1dd3d" }, "raised_back_of_hand_tone2": { "category": "people", "moji": "🤚🏼", + "description": "raised back of hand tone 2", "unicodeVersion": "9.0", "digest": "429ed19555c9e5197b729b3e7bd8013346551051cb0b3fbc8a4372717c9a027d" }, "raised_back_of_hand_tone3": { "category": "people", "moji": "🤚🏽", + "description": "raised back of hand tone 3", "unicodeVersion": "9.0", "digest": "487a1c3f19e77c99b520ec073de2acc4a9e585b739a84b3989f7de85d2c2045c" }, "raised_back_of_hand_tone4": { "category": "people", "moji": "🤚🏾", + "description": "raised back of hand tone 4", "unicodeVersion": "9.0", "digest": "154254d8500c55ec3de698be4a352f9bcf06e2950cabc4eabaccad0f39a1e1e9" }, "raised_back_of_hand_tone5": { "category": "people", "moji": "🤚🏿", + "description": "raised back of hand tone 5", "unicodeVersion": "9.0", "digest": "6e9c0855ecd5f14adca5e5862427c3d39ffcf86f7ddd3aaa1fefc3cefc7483c8" }, "raised_hand": { "category": "people", "moji": "✋", + "description": "raised hand", "unicodeVersion": "6.0", "digest": "5cf11be683aea985d5ba51fbd44722c2327311bfe26b61c3d441c90f5d5a195a" }, "raised_hand_tone1": { "category": "people", "moji": "✋🏻", + "description": "raised hand tone 1", "unicodeVersion": "8.0", "digest": "865afca29b57577fed8fe8c2be57b74254a008c8cf34194680be2759239b5f5d" }, "raised_hand_tone2": { "category": "people", "moji": "✋🏼", + "description": "raised hand tone 2", "unicodeVersion": "8.0", "digest": "832169a0b626a682a58a3b998f68413657b4962c1fab05f1fdc2668e82727210" }, "raised_hand_tone3": { "category": "people", "moji": "✋🏽", + "description": "raised hand tone 3", "unicodeVersion": "8.0", "digest": "3959a873ad7671de82c615c4ed840b011e67baafb2bab7dd16859608d3e83cb1" }, "raised_hand_tone4": { "category": "people", "moji": "✋🏾", + "description": "raised hand tone 4", "unicodeVersion": "8.0", "digest": "db542f65d076ccf3dbfca27cb7c2f135a8bf7a487a81a04873e70172bdfcd579" }, "raised_hand_tone5": { "category": "people", "moji": "✋🏿", + "description": "raised hand tone 5", "unicodeVersion": "8.0", "digest": "88ca884d14baaae48df21d75c22d82fb15bdc395e42026f5ca34cd65e5ae8674" }, "raised_hands": { "category": "people", "moji": "🙌", + "description": "person raising both hands in celebration", "unicodeVersion": "6.0", "digest": "2ee73466a3f5079e542857fe6f5497e9f87753a81854985ce3356a8d3da1d8b8" }, "raised_hands_tone1": { "category": "people", "moji": "🙌🏻", + "description": "person raising both hands in celebration tone 1", "unicodeVersion": "8.0", "digest": "43e73c60f040a66374b8ec98f3629a90d13ae9f472446ed7676cd5573e824f4b" }, "raised_hands_tone2": { "category": "people", "moji": "🙌🏼", + "description": "person raising both hands in celebration tone 2", "unicodeVersion": "8.0", "digest": "fcc5255bb2b06dc82d6878e74cf34e8ce118c70004a06d39a980683772b98c52" }, "raised_hands_tone3": { "category": "people", "moji": "🙌🏽", + "description": "person raising both hands in celebration tone 3", "unicodeVersion": "8.0", "digest": "3ee3e0aafef486e766a166935e8147fb75a7329cfebc96dec876cc45e83a8754" }, "raised_hands_tone4": { "category": "people", "moji": "🙌🏾", + "description": "person raising both hands in celebration tone 4", "unicodeVersion": "8.0", "digest": "78a8cbf6b2b85be4d6b18f0ff6a77f197963117955725fb7e57e0441effb928f" }, "raised_hands_tone5": { "category": "people", "moji": "🙌🏿", + "description": "person raising both hands in celebration tone 5", "unicodeVersion": "8.0", "digest": "2a5ed7334a17172db0cd820a559e7f75df40ec44de6c25d194c76e1b58c634cb" }, "raising_hand": { "category": "people", "moji": "🙋", + "description": "happy person raising one hand", "unicodeVersion": "6.0", "digest": "512750b00704f1ccefd3c757743540b785ad7670dbbe4a2c4dca8d93e6701920" }, "raising_hand_tone1": { "category": "people", "moji": "🙋🏻", + "description": "happy person raising one hand tone1", "unicodeVersion": "8.0", "digest": "2897722f091c273dd3714cff7423c2475bc3070416c28014ca03322b9ece48bc" }, "raising_hand_tone2": { "category": "people", "moji": "🙋🏼", + "description": "happy person raising one hand tone2", "unicodeVersion": "8.0", "digest": "59199b334b3845911382c1f29bd7c0d5ef9d2486417345e265b166ead7d3e1c1" }, "raising_hand_tone3": { "category": "people", "moji": "🙋🏽", + "description": "happy person raising one hand tone3", "unicodeVersion": "8.0", "digest": "f95b338d5efcf14ef12f415a2c1bba93df48628ddc94f34f70c31e1b3c2e1d28" }, "raising_hand_tone4": { "category": "people", "moji": "🙋🏾", + "description": "happy person raising one hand tone4", "unicodeVersion": "8.0", "digest": "951ddbfdb57d5a60551b59b3d0f7ca00a64912f4a101a73afaebd68445cd6cec" }, "raising_hand_tone5": { "category": "people", "moji": "🙋🏿", + "description": "happy person raising one hand tone5", "unicodeVersion": "8.0", "digest": "9370f93704d8f89ca6dc946715eab5e7dba82bf04dd68c00f5c0abb8bc16371e" }, "ram": { "category": "nature", "moji": "🐏", + "description": "ram", "unicodeVersion": "6.0", "digest": "2875ab28e1018b39062aeb0c5ce488c48a98f13e9f2364470a0a700b126604f2" }, "ramen": { "category": "food", "moji": "🍜", + "description": "steaming bowl", "unicodeVersion": "6.0", "digest": "425662a49c4c13577c0de8d45d004e5ba204aaadbaabae62a5c283ecd7a9a2c5" }, "rat": { "category": "nature", "moji": "🐀", + "description": "rat", "unicodeVersion": "6.0", "digest": "14380d65498c6ce037c02a93bca2b24f25a368d85278d6015b8c9f7cd261f8e2" }, "record_button": { "category": "symbols", "moji": "⏺", + "description": "black circle for record", "unicodeVersion": "7.0", "digest": "92be12161ba206bb2e06a39131711c7b17368d55b4aae0b48f0ac5b6b1cde76b" }, "recycle": { "category": "symbols", "moji": "♻", + "description": "black universal recycling symbol", "unicodeVersion": "3.2", "digest": "c377e8537367b05b5de9be860a0fcabd7aed2bf4ba146eefc423671a21530369" }, "red_car": { "category": "travel", "moji": "🚗", + "description": "automobile", "unicodeVersion": "6.0", "digest": "8a99832a195263c0e922af53d52dea37aa3e07032b3c2a1977f8527b4a144b9c" }, "red_circle": { "category": "symbols", "moji": "🔴", + "description": "large red circle", "unicodeVersion": "6.0", "digest": "9dcf0132f6f2cc81702f0e3b15b37984e8439796705bf98f68ba449b3dfa5307" }, "registered": { "category": "symbols", "moji": "®", + "description": "registered sign", "unicodeVersion": "1.1", "digest": "9661b1df529ecb752d130820c55c403e5de263748eb02f7fea327818bc282d94" }, "relaxed": { "category": "people", "moji": "☺", + "description": "white smiling face", "unicodeVersion": "1.1", "digest": "2d5aed4fb8504c6d6660ef8d3bfe0cc053dcd6099c2f53748c202dc970c639bc" }, "relieved": { "category": "people", "moji": "😌", + "description": "relieved face", "unicodeVersion": "6.0", "digest": "b4ce2ba6c220d887fe5e333c05ed773df9b6df0ac456879fd8f5103ff68604a5" }, "reminder_ribbon": { "category": "activity", "moji": "🎗", + "description": "reminder ribbon", "unicodeVersion": "7.0", "digest": "c3de2a7c9350b77a0b86c0dcce9dcd9953ea8a97aa1e7aed149755924742f54d" }, "repeat": { "category": "symbols", "moji": "🔁", + "description": "clockwise rightwards and leftwards open circle arr", "unicodeVersion": "6.0", "digest": "b9512d508613ed0eb3181eb1030f7f6fd6b994476ecdfa308733c6df975fb99e" }, "repeat_one": { "category": "symbols", "moji": "🔂", + "description": "clockwise rightwards and leftwards open circle arr", "unicodeVersion": "6.0", "digest": "53409cf24dd4bb0d7b50ae359f15d06b87b7f4a292ed5c3a09652fa421a90bf2" }, "restroom": { "category": "symbols", "moji": "🚻", + "description": "restroom", "unicodeVersion": "6.0", "digest": "2e7a1bfc9a9d49b0272230a91db7369e24d54bf1de8e683d36b85f1d8c037f77" }, "revolving_hearts": { "category": "symbols", "moji": "💞", + "description": "revolving hearts", "unicodeVersion": "6.0", "digest": "c43d3197cb4cf06659f643638f6c4e91a2889e0f6531b7d81ea826c2a8b784fc" }, "rewind": { "category": "symbols", "moji": "⏪", + "description": "black left-pointing double triangle", "unicodeVersion": "6.0", "digest": "d20c918c1e528ff0947312738501ca9a6fb6ff4016aad07db7a8125d00fd65cd" }, "rhino": { "category": "nature", "moji": "🦏", + "description": "rhinoceros", "unicodeVersion": "9.0", "digest": "163fa3acd78eead72c431a1f48b8465a6d45272a9169560e456d30b4df93dc6b" }, "ribbon": { "category": "objects", "moji": "🎀", + "description": "ribbon", "unicodeVersion": "6.0", "digest": "74315fe907f9f0203afe139cd4552aa442eecfa2a64fac12db3e1292fc5a8828" }, "rice": { "category": "food", "moji": "🍚", + "description": "cooked rice", "unicodeVersion": "6.0", "digest": "f544f12606de59d28739798003f14ebd8869856add8e24496ec5dda3e131daf4" }, "rice_ball": { "category": "food", "moji": "🍙", + "description": "rice ball", "unicodeVersion": "6.0", "digest": "2cba6f5364cd366859bc8948897b65fc97b225ea7973d9be3b24aba388fed8e8" }, "rice_cracker": { "category": "food", "moji": "🍘", + "description": "rice cracker", "unicodeVersion": "6.0", "digest": "ac0f805d41d4f322154c1968bd3ce3e9aabcd39d908182e52fd7d28458dbef92" }, "rice_scene": { "category": "travel", "moji": "🎑", + "description": "moon viewing ceremony", "unicodeVersion": "6.0", "digest": "b942a06d3da0570aca59bab0af57cd8c16863934f12a38f70339fd0a36f675f5" }, "right_facing_fist": { "category": "people", "moji": "🤜", + "description": "right-facing fist", "unicodeVersion": "9.0", "digest": "f815d1cc0c0345ddcc8886ae9c133582d7dc779732ac9b93dde1ab4fdd3b251d" }, "right_facing_fist_tone1": { "category": "people", "moji": "🤜🏻", + "description": "right facing fist tone 1", "unicodeVersion": "9.0", "digest": "0f9269b70cf68071d97389e059a2bdacffd73f2afd2ce6cfd7447bb1a4e9abbb" }, "right_facing_fist_tone2": { "category": "people", "moji": "🤜🏼", + "description": "right facing fist tone 2", "unicodeVersion": "9.0", "digest": "32a9833db853972e49e65aa227fb0512c57362da190aa1cc40e1d64f238e837e" }, "right_facing_fist_tone3": { "category": "people", "moji": "🤜🏽", + "description": "right facing fist tone 3", "unicodeVersion": "9.0", "digest": "be4706f8bb088411f5cbbf9065a0ae5b773c97456bd975c2b6789765657847b9" }, "right_facing_fist_tone4": { "category": "people", "moji": "🤜🏾", + "description": "right facing fist tone 4", "unicodeVersion": "9.0", "digest": "1680862891a9d85c4b6f76232a80e2ef7428bcec93087c86eae2efaba9c6a3f7" }, "right_facing_fist_tone5": { "category": "people", "moji": "🤜🏿", + "description": "right facing fist tone 5", "unicodeVersion": "9.0", "digest": "388715a4bc2178c52bbb3bc2729f57be50acab5d751784c9f3220e86c6b1fbcc" }, "ring": { "category": "people", "moji": "💍", + "description": "ring", "unicodeVersion": "6.0", "digest": "b5322907222797b5e1786209cda88513e76cd397a40f0a7da24847245c95ef9d" }, "robot": { "category": "people", "moji": "🤖", + "description": "robot face", "unicodeVersion": "8.0", "digest": "4d788e6ec89279588b036fca6b17f5a981291681df8f90306ecf5c039de40848" }, "rocket": { "category": "travel", "moji": "🚀", + "description": "rocket", "unicodeVersion": "6.0", "digest": "b82e68a95aa89a6de344d6e256fef86a848ebc91de560b043b3e1f7fd072d57d" }, "rofl": { "category": "people", "moji": "🤣", + "description": "rolling on the floor laughing", "unicodeVersion": "9.0", "digest": "f4f99ba2ac67b97338f904f9384ff03fb832a2e427bf6e74611bf5fee45f1f48" }, "roller_coaster": { "category": "travel", "moji": "🎢", + "description": "roller coaster", "unicodeVersion": "6.0", "digest": "a65e9ace1d7900499777af1225995f17af90a398bb414764c20b6e09a8c23a2c" }, "rolling_eyes": { "category": "people", "moji": "🙄", + "description": "face with rolling eyes", "unicodeVersion": "8.0", "digest": "23dea8100da488a05721a4e82823eb438393b0ea762211c9ecef011d127aa1b7" }, "rooster": { "category": "nature", "moji": "🐓", + "description": "rooster", "unicodeVersion": "6.0", "digest": "2b90c5cf6fa46da13eb77285443d600afcea0c48bd1d215d60167e7dc510da5d" }, "rose": { "category": "nature", "moji": "🌹", + "description": "rose", "unicodeVersion": "6.0", "digest": "73799e459dba188de4de704605d824242feeb65d587c5bf9109acf528d037146" }, "rosette": { "category": "activity", "moji": "🏵", + "description": "rosette", "unicodeVersion": "7.0", "digest": "2537def4deef422d4e669b28b1a0675259306ab38601019df3ec3482b14e52d5" }, "rotating_light": { "category": "travel", "moji": "🚨", + "description": "police cars revolving light", "unicodeVersion": "6.0", "digest": "91fcdb85a752ae904d335a978c7e7936aed4c75d414b35219b5a74430e51555f" }, "round_pushpin": { "category": "objects", "moji": "📍", + "description": "round pushpin", "unicodeVersion": "6.0", "digest": "8ffca77bbdc6f1f726daf3abd6eff338a5ad1aa9b09dbbd8782c1e7ef5452f30" }, "rowboat": { "category": "activity", "moji": "🚣", + "description": "rowboat", "unicodeVersion": "6.0", "digest": "83715d83a061926d4ad3bb569b21f5d337e3ebd4c9bcdfe493e661c12adc0a16" }, "rowboat_tone1": { "category": "activity", "moji": "🚣🏻", + "description": "rowboat tone 1", "unicodeVersion": "8.0", "digest": "e279ac816442c0876fba1f42c700b80f2fb6de671e1a8a9e9d11b71eed5c58e8" }, "rowboat_tone2": { "category": "activity", "moji": "🚣🏼", + "description": "rowboat tone 2", "unicodeVersion": "8.0", "digest": "6a48eba352ed4971d26498b6c622e5772389c89c5205ed02acde8e995dddcc3b" }, "rowboat_tone3": { "category": "activity", "moji": "🚣🏽", + "description": "rowboat tone 3", "unicodeVersion": "8.0", "digest": "875948f6d8354ebd95ce9a66fde30f06a8366dcd89d5ca3e660845f8801e9305" }, "rowboat_tone4": { "category": "activity", "moji": "🚣🏾", + "description": "rowboat tone 4", "unicodeVersion": "8.0", "digest": "8c7ac7346b0020d0ff5e2f4a1efb1b7785eac637f17556663ec33e2335083f0a" }, "rowboat_tone5": { "category": "activity", "moji": "🚣🏿", + "description": "rowboat tone 5", "unicodeVersion": "8.0", "digest": "a399dbb647892b22323e0bf17bc36a9b5f1708ebedf9ba525233ee7b9d48339a" }, "rugby_football": { "category": "activity", "moji": "🏉", + "description": "rugby football", "unicodeVersion": "6.0", "digest": "cc6f00ade3e0bbb7899e7bfb138b57216dd66de26d7967d5ffa501f382ed09f4" }, "runner": { "category": "people", "moji": "🏃", + "description": "runner", "unicodeVersion": "6.0", "digest": "e9af7b591be60ade2049dbada0f062ba2d3e17f02bec76cbd34ce68854a2a10c" }, "runner_tone1": { "category": "people", "moji": "🏃🏻", + "description": "runner tone 1", "unicodeVersion": "8.0", "digest": "21091cbb09c558712ecf63548bf28b7995df42bdb85235088799a517800e52f5" }, "runner_tone2": { "category": "people", "moji": "🏃🏼", + "description": "runner tone 2", "unicodeVersion": "8.0", "digest": "1fe3d194f675a46fe67799394192e66c407dd81163363692c5e7da32ddb9af2b" }, "runner_tone3": { "category": "people", "moji": "🏃🏽", + "description": "runner tone 3", "unicodeVersion": "8.0", "digest": "8cea1bf4ef3be71f42dc5bae978d5b7a197a3851543225349ef0dda29a370537" }, "runner_tone4": { "category": "people", "moji": "🏃🏾", + "description": "runner tone 4", "unicodeVersion": "8.0", "digest": "c33f0b8b5a71d295fb6ba322e79446964a8eca9e4573efd591e4273808b088a0" }, "runner_tone5": { "category": "people", "moji": "🏃🏿", + "description": "runner tone 5", "unicodeVersion": "8.0", "digest": "9f59e6dd0fdf2f17bceb41f5c355b4e6f3c8bb8cbd8af0992f0b5630ff8892e8" }, "running_shirt_with_sash": { "category": "activity", "moji": "🎽", + "description": "running shirt with sash", "unicodeVersion": "6.0", "digest": "7542307d3595aca45e8ccae66b6e58b6e92870144b738263d5379ec6dc992b76" }, "sa": { "category": "symbols", "moji": "🈂", + "description": "squared katakana sa", "unicodeVersion": "6.0", "digest": "6042bcabd1516ef3847d695aba22851c49421244432d256e24eba04e8a223dab" }, "sagittarius": { "category": "symbols", "moji": "♐", + "description": "sagittarius", "unicodeVersion": "1.1", "digest": "a02593e025023f2e82a01c587a8c0bbb1eff88cbcabf535a1558413eb32ed1d5" }, "sailboat": { "category": "travel", "moji": "⛵", + "description": "sailboat", "unicodeVersion": "5.2", "digest": "c95ef4dc939cbdcb757ef3cd90331310e8c0a426add8cc800bae2540148a3195" }, "sake": { "category": "food", "moji": "🍶", + "description": "sake bottle and cup", "unicodeVersion": "6.0", "digest": "0a786075f3d9da48ae91afccf6ae0d097888da9509d354ee1d3cb99afcc88fe4" }, "salad": { "category": "food", "moji": "🥗", + "description": "green salad", "unicodeVersion": "9.0", "digest": "fe321487ab847abe670e68a83f1d9e096129741c689c769ee7de4a65aeac29f8" }, "sandal": { "category": "people", "moji": "👡", + "description": "womans sandal", "unicodeVersion": "6.0", "digest": "03c3077cb4bd900934f9bdf921165b465e5cc9a6bee53e45a091411bceb8892d" }, "santa": { "category": "people", "moji": "🎅", + "description": "father christmas", "unicodeVersion": "6.0", "digest": "178513e3d815917e59958870f5885b3414b43a16b8056980c863a468dfe00179" }, "santa_tone1": { "category": "people", "moji": "🎅🏻", + "description": "father christmas tone 1", "unicodeVersion": "8.0", "digest": "bf900bbc19bbd329229add9326e28e8197b69d6ddceb69f42162b0200fde5d16" }, "santa_tone2": { "category": "people", "moji": "🎅🏼", + "description": "father christmas tone 2", "unicodeVersion": "8.0", "digest": "7340f2171adab97198e3eecac8b0d84c4c2a41f84606301a0d10e9fe655c93d1" }, "santa_tone3": { "category": "people", "moji": "🎅🏽", + "description": "father christmas tone 3", "unicodeVersion": "8.0", "digest": "7368ab75454ec28d8f7d6baef6ad69b5278445a9f50753f6624731bffde32054" }, "santa_tone4": { "category": "people", "moji": "🎅🏾", + "description": "father christmas tone 4", "unicodeVersion": "8.0", "digest": "0ee60188353e0ee7772079c192bebbc6d49e74e63906f840c66da4eb35f4f245" }, "santa_tone5": { "category": "people", "moji": "🎅🏿", + "description": "father christmas tone 5", "unicodeVersion": "8.0", "digest": "e4378a0cc5d21e9b9fe6e35c32d1ebc6fb8c2e1c09554cd096aeaefd3a6eb511" }, "satellite": { "category": "objects", "moji": "📡", + "description": "satellite antenna", "unicodeVersion": "6.0", "digest": "c9d63118dcb445856917bb080460ab695cc78e715dcbba30ba18dffa9e906b27" }, "satellite_orbital": { "category": "travel", "moji": "🛰", + "description": "satellite", "unicodeVersion": "7.0", "digest": "beb2f50e7f2b010e76bed9daa95d7329a93c783d3ebc4f0b797dd721c5e3d32d" }, "saxophone": { "category": "activity", "moji": "🎷", + "description": "saxophone", "unicodeVersion": "6.0", "digest": "dfd138634f6702a3b89b5a2a50016720eef3f800b0d1d8c9fe097808c9491e96" }, "scales": { "category": "objects", "moji": "⚖", + "description": "scales", "unicodeVersion": "4.1", "digest": "2280c026f16c6b92e0daa00bc14e718770f8d231c571ab439bde84d837cf31cc" }, "school": { "category": "travel", "moji": "🏫", + "description": "school", "unicodeVersion": "6.0", "digest": "af198b068a86ccad3daec4c6873e6b4735086c1ecbb3848182e70bae9aa3ee24" }, "school_satchel": { "category": "people", "moji": "🎒", + "description": "school satchel", "unicodeVersion": "6.0", "digest": "f670ae8aea67eb9d8aaa0bf2748c1cc3e503dcc1dbe999133afcdf21af046b24" }, "scissors": { "category": "objects", "moji": "✂", + "description": "black scissors", "unicodeVersion": "1.1", "digest": "95225be28f05d8b5a6b6e6bf58d973f61f183ad4fef55a558dc1b810796b85c8" }, "scooter": { "category": "travel", "moji": "🛴", + "description": "scooter", "unicodeVersion": "9.0", "digest": "4a7db148880398db75e059711cb53edefb6b8fa9d442009f52856b887ab1dde4" }, "scorpion": { "category": "nature", "moji": "🦂", + "description": "scorpion", "unicodeVersion": "8.0", "digest": "d41119d1ea5daf727c17dbea7dadec1718c72fc9f98ae88252161df5fde0938a" }, "scorpius": { "category": "symbols", "moji": "♏", + "description": "scorpius", "unicodeVersion": "1.1", "digest": "a36404b408814c2ecb8fa8b61f5c5432dfcf54cae8c09cc67b8d0fadf7cbdc03" }, "scream": { "category": "people", "moji": "😱", + "description": "face screaming in fear", "unicodeVersion": "6.0", "digest": "916e4903a4b694da4b00f190f872a4e100e7736b7a2e6171fa1636f46bf646e6" }, "scream_cat": { "category": "people", "moji": "🙀", + "description": "weary cat face", "unicodeVersion": "6.0", "digest": "f1d3a6ff538064e7d5e0321bbc33aba44e8da703dc1894ef1403c0cd6d63d781" }, "scroll": { "category": "objects", "moji": "📜", + "description": "scroll", "unicodeVersion": "6.0", "digest": "9b2cb00860bcc2d20017cafb2ed9681b6232dc07273d489d75d53ce29e4ba3ab" }, "seat": { "category": "travel", "moji": "💺", + "description": "seat", "unicodeVersion": "6.0", "digest": "ae68d86fc2a07cae332451b23bd1ceba3f6526a6c56d8c1089777fa4632850e1" }, "second_place": { "category": "activity", "moji": "🥈", + "description": "second place medal", "unicodeVersion": "9.0", "digest": "9e2336fc16e532829b55380252f94655b58817d47c909fc2570002c5b06b9c40" }, "secret": { "category": "symbols", "moji": "㊙", + "description": "circled ideograph secret", "unicodeVersion": "1.1", "digest": "1d0b9adde2657f41421b135962de20820cf4b4eb0204044f9859522ab9d211b0" }, "see_no_evil": { "category": "nature", "moji": "🙈", + "description": "see-no-evil monkey", "unicodeVersion": "6.0", "digest": "3ff66d2e84b36d071d0a34f8e41cfd620a56b83131474ea50ed7803b635551ed" }, "seedling": { "category": "nature", "moji": "🌱", + "description": "seedling", "unicodeVersion": "6.0", "digest": "c0ec5e6d20e1afdc4e78eeddb1301c8b708ad6278e7287a4e4e825417c858e75" }, "selfie": { "category": "people", "moji": "🤳", + "description": "selfie", "unicodeVersion": "9.0", "digest": "2a1bc9f18ad4d6fb893d91c88ef1b2d9bd063dc2bb1a4b08c248c30f52545d4e" }, "selfie_tone1": { "category": "people", "moji": "🤳🏻", + "description": "selfie tone 1", "unicodeVersion": "9.0", "digest": "26dc212ffed30c276bd6a66a72bc4513e68098a2205fb4ca5b51ccfa1de5b544" }, "selfie_tone2": { "category": "people", "moji": "🤳🏼", + "description": "selfie tone 2", "unicodeVersion": "9.0", "digest": "71eceaefda46e3521f374f76693e7fa8f215067498067900080e2925ca94d7de" }, "selfie_tone3": { "category": "people", "moji": "🤳🏽", + "description": "selfie tone 3", "unicodeVersion": "9.0", "digest": "53eabbd4f6b8ebbd2f7af7bf5cd64309c4039ac1c5b2180290a547deaafcebdf" }, "selfie_tone4": { "category": "people", "moji": "🤳🏾", + "description": "selfie tone 4", "unicodeVersion": "9.0", "digest": "0baad378b09652b99c5d458db2e03b4db14a1557db4ea0969806a0ca1d33d40c" }, "selfie_tone5": { "category": "people", "moji": "🤳🏿", + "description": "selfie tone 5", "unicodeVersion": "9.0", "digest": "9a07608f34ec4dad48764a855f83f3965709d7b2fd2342e6dc9ed61f23f4adfd" }, "seven": { "category": "symbols", "moji": "7️⃣", + "description": "keycap digit seven", "unicodeVersion": "3.0", "digest": "ae85172d2c76c44afb4e3b45d277d400abb2dc895244b9abfbd1dac1cd7c53c2" }, "shallow_pan_of_food": { "category": "food", "moji": "🥘", + "description": "shallow pan of food", "unicodeVersion": "9.0", "digest": "7c7ad9d5d3f7226427d310b5853e8257fad899febe58dcbc5adb4677964f5c6d" }, "shamrock": { "category": "nature", "moji": "☘", + "description": "shamrock", "unicodeVersion": "4.1", "digest": "68ed70c26e04a818439a1742d2da6bc169edd02db86b6e6f8014b651f3235488" }, "shark": { "category": "nature", "moji": "🦈", + "description": "shark", "unicodeVersion": "9.0", "digest": "23a2364b6356e7bbb84c138e9cf58e2c68cd8caabb337a0c4d365ce87bf5d2da" }, "shaved_ice": { "category": "food", "moji": "🍧", + "description": "shaved ice", "unicodeVersion": "6.0", "digest": "54048e77268b7548d03088517bf8558d11324db901ca57f9bec93f1873663a74" }, "sheep": { "category": "nature", "moji": "🐑", + "description": "sheep", "unicodeVersion": "6.0", "digest": "c867c8e6e51768f1f51f4fe5abd3fbd5c1d69b01a3cb48b5fb94b6e2338a271c" }, "shell": { "category": "nature", "moji": "🐚", + "description": "spiral shell", "unicodeVersion": "6.0", "digest": "8983652d33ad6ab91195518cecb5a268a1c0ae603d271f0ddd756ff50058ddb3" }, "shield": { "category": "objects", "moji": "🛡", + "description": "shield", "unicodeVersion": "7.0", "digest": "763d0a56a62c51c730ccb0fbea38ab597cbf41a85ab968198e6ec35630d50aa5" }, "shinto_shrine": { "category": "travel", "moji": "⛩", + "description": "shinto shrine", "unicodeVersion": "5.2", "digest": "38a6d756c5aa9703510afa5076d75192f7814bbb6632394d4b8253d9ceda7f8c" }, "ship": { "category": "travel", "moji": "🚢", + "description": "ship", "unicodeVersion": "6.0", "digest": "79c680845892a3e81ec6af2160ee07c29147155943e5daba6c76d04252014c20" }, "shirt": { "category": "people", "moji": "👕", + "description": "t-shirt", "unicodeVersion": "6.0", "digest": "46c7253e15d7cac03699ddb1550fbb7565bbe487310f7e218c0583aa69f9d3c5" }, "shopping_bags": { "category": "objects", "moji": "🛍", + "description": "shopping bags", "unicodeVersion": "7.0", "digest": "95a3f03c675207bb1354270d02a630c204455c47b3edca23c48523a40cf3ea3b" }, "shopping_cart": { "category": "objects", "moji": "🛒", + "description": "shopping trolley", "unicodeVersion": "9.0", "digest": "4599b63f6861cdb4d8272cac84435c24c1d4d6a73c66d51e04a1cd14a1d333e6" }, "shower": { "category": "objects", "moji": "🚿", + "description": "shower", "unicodeVersion": "6.0", "digest": "6b3c767c0eb472d4861c6c3cc2735a5e2c09681872ef42a11dc89f3c80b9da01" }, "shrimp": { "category": "nature", "moji": "🦐", + "description": "shrimp", "unicodeVersion": "9.0", "digest": "b3651f3be3767125076a013fe903854f5b456a8afae865cb219cf528e0f44caa" }, "shrug": { "category": "people", "moji": "🤷", + "description": "shrug", "unicodeVersion": "9.0", "digest": "6e264243cc3b6e396069dea4357a958bdcd4081cb1af0ed6aa47235bef88cf27" }, "shrug_tone1": { "category": "people", "moji": "🤷🏻", + "description": "shrug tone 1", "unicodeVersion": "9.0", "digest": "0567b9fd95c8a857914003a5465a500ca79c8111811d45b865021b1b1d92d0b1" }, "shrug_tone2": { "category": "people", "moji": "🤷🏼", + "description": "shrug tone 2", "unicodeVersion": "9.0", "digest": "1557c2f5e3d4599c806d74c0b78afcca940678787534b6862bb89a20601bac8a" }, "shrug_tone3": { "category": "people", "moji": "🤷🏽", + "description": "shrug tone 3", "unicodeVersion": "9.0", "digest": "f02754541a7bf74ba7eebe6c27daf1e3e1dac25172c35b8ba45641e278dfda3d" }, "shrug_tone4": { "category": "people", "moji": "🤷🏾", + "description": "shrug tone 4", "unicodeVersion": "9.0", "digest": "2b5121164cb5f4e253d8fb31f6445cf8afaf30dba41732edc511440cdb78d15c" }, "shrug_tone5": { "category": "people", "moji": "🤷🏿", + "description": "shrug tone 5", "unicodeVersion": "9.0", "digest": "62d99a26bbad479f574f66208c41b9960cd41fb9d79d3a13fbdaa44682077115" }, "signal_strength": { "category": "symbols", "moji": "📶", + "description": "antenna with bars", "unicodeVersion": "6.0", "digest": "2c6f04ba4ecd2d2d423e19eb52cfbfd253f4db6e0908d91c1af4ea6192597447" }, "six": { "category": "symbols", "moji": "6️⃣", + "description": "keycap digit six", "unicodeVersion": "3.0", "digest": "cede9324261208d0fd5d00fcdfc0df0331944bd9cff4f40b30a582a641526c1c" }, "six_pointed_star": { "category": "symbols", "moji": "🔯", + "description": "six pointed star with middle dot", "unicodeVersion": "6.0", "digest": "9203e3b4f08af439ae0bfb6a7b29a02dceb027b6c2dc5463b524dfd314cbff4e" }, "ski": { "category": "activity", "moji": "🎿", + "description": "ski and ski boot", "unicodeVersion": "6.0", "digest": "80f0ca8660ba373fef823af9e98e148c4ddb1e217eb6d0a0ea2bae2288b57570" }, "skier": { "category": "activity", "moji": "⛷", + "description": "skier", "unicodeVersion": "5.2", "digest": "4fff0aa155367f551a59aed9657b8afa159173882b25db9cd8434293d1eed76d" }, "skull": { "category": "people", "moji": "💀", + "description": "skull", "unicodeVersion": "6.0", "digest": "cdd2031164281bf2b0083df4479651d96bc16d11e44bac4deaf402a9c0d6f40a" }, "skull_crossbones": { "category": "objects", "moji": "☠", + "description": "skull and crossbones", "unicodeVersion": "1.1", "digest": "ae764ba21a1fcc4409f4cc9e75a261d70b87548f64158dbd3451374ad5724123" }, "sleeping": { "category": "people", "moji": "😴", + "description": "sleeping face", "unicodeVersion": "6.1", "digest": "1050a011509b56735c9f30a6fccc876256e2a4546dc6052e518151c8aca4b526" }, "sleeping_accommodation": { "category": "objects", "moji": "🛌", + "description": "sleeping accommodation", "unicodeVersion": "7.0", "digest": "2ce42c027d1d0947abc403c359fd668a7bc44f5ead2582e97f3db7dd4e22e5d5" }, "sleepy": { "category": "people", "moji": "😪", + "description": "sleepy face", "unicodeVersion": "6.0", "digest": "2ee9bb1f72ef99e0e33095ec2bbf7a58ffea0ff7d40b840f4cdba57be9de74b0" }, "slight_frown": { "category": "people", "moji": "🙁", + "description": "slightly frowning face", "unicodeVersion": "7.0", "digest": "d71d564a6c2d366a8e28a78ef4e07d387a77037fe8c99aa0ea1571299dc490c9" }, "slight_smile": { "category": "people", "moji": "🙂", + "description": "slightly smiling face", "unicodeVersion": "7.0", "digest": "10f4b66a755f5c78762a330f20d1866e4a22f3f1d495161d758d3bab8d2f36fe" }, "slot_machine": { "category": "activity", "moji": "🎰", + "description": "slot machine", "unicodeVersion": "6.0", "digest": "914184788f8cd865cd074dca25c22acee31f5498117bd9a6e78cae67e6601652" }, "small_blue_diamond": { "category": "symbols", "moji": "🔹", + "description": "small blue diamond", "unicodeVersion": "6.0", "digest": "0b56d8e6b5ddf1f49fcc76e45e5fb2ee9f99ae6ffe682c26eaea4d9b7faac36c" }, "small_orange_diamond": { "category": "symbols", "moji": "🔸", + "description": "small orange diamond", "unicodeVersion": "6.0", "digest": "a2235830550e289c1608f2dcf5ede48f5c1a0eff45570699c39708c9677ab950" }, "small_red_triangle": { "category": "symbols", "moji": "🔺", + "description": "up-pointing red triangle", "unicodeVersion": "6.0", "digest": "8c2985c4e9ce42d2f3b35539b879bc36206c5ef749f39fbd1eac51bd2676e1e5" }, "small_red_triangle_down": { "category": "symbols", "moji": "🔻", + "description": "down-pointing red triangle", "unicodeVersion": "6.0", "digest": "46bd328df2fbf5d0597596bbf00d2d5f6e0c65bcb8f3fb325df8ba0c25e445b5" }, "smile": { "category": "people", "moji": "😄", + "description": "smiling face with open mouth and smiling eyes", "unicodeVersion": "6.0", "digest": "14905c372d5bf7719bd727c9efae31a03291acec79801652a23710c6848c5d14" }, "smile_cat": { "category": "people", "moji": "😸", + "description": "grinning cat face with smiling eyes", "unicodeVersion": "6.0", "digest": "c35b76d6df100edb4022d762f47abfeb9f5e70886960c1d25908bd5d57ccb47e" }, "smiley": { "category": "people", "moji": "😃", + "description": "smiling face with open mouth", "unicodeVersion": "6.0", "digest": "a89f31eb9d814636852517a7f4eadec59195e2ac2cc9f8d124f1a1cc0f775b4a" }, "smiley_cat": { "category": "people", "moji": "😺", + "description": "smiling cat face with open mouth", "unicodeVersion": "6.0", "digest": "3e66a113c5e3e73fb94be29084cb27986b6bdb0e78ab44785bf2a35a550e71bf" }, "smiling_imp": { "category": "people", "moji": "😈", + "description": "smiling face with horns", "unicodeVersion": "6.0", "digest": "3e02131d16525938f6facc7e097365dec7e13c8a0049a3be35fc29c80cc291b3" }, "smirk": { "category": "people", "moji": "😏", + "description": "smirking face", "unicodeVersion": "6.0", "digest": "3c180d46f5574d6fca3bb68eb02517da60b7008843cb3e90f2f9620d0c8ee943" }, "smirk_cat": { "category": "people", "moji": "😼", + "description": "cat face with wry smile", "unicodeVersion": "6.0", "digest": "0683c7f73e1f65984e91313607d7cca21d99acd4b2e9932f00e0fffd0ce90742" }, "smoking": { "category": "objects", "moji": "🚬", + "description": "smoking symbol", "unicodeVersion": "6.0", "digest": "baa9cb444bf0fe5c74358f981b19bc9e5c0415ced7f042baf93642282476ea61" }, "snail": { "category": "nature", "moji": "🐌", + "description": "snail", "unicodeVersion": "6.0", "digest": "5733bf3672ae4b2b3e090fa670aeac70dcbcc04ca5b13abc8c8e53b8b3d4ff33" }, "snake": { "category": "nature", "moji": "🐍", + "description": "snake", "unicodeVersion": "6.0", "digest": "18da2d97c771149ef5454dd23470e900903a62ab93f9e2ce301aad5a8181d773" }, "sneezing_face": { "category": "people", "moji": "🤧", + "description": "sneezing face", "unicodeVersion": "9.0", "digest": "c20ef571dc7e35572fe3c18b7845aefc89af083ea925c48a29de3b7387af6e17" }, "snowboarder": { "category": "activity", "moji": "🏂", + "description": "snowboarder", "unicodeVersion": "6.0", "digest": "c6e074139b851aa53b1ba6464d84da14b3da7412fc44c6c196a8469d76915c19" }, "snowflake": { "category": "nature", "moji": "❄", + "description": "snowflake", "unicodeVersion": "1.1", "digest": "6556c918e181df01ba849e76c43972d5310439971e5d8fc2409d112c05bf0028" }, "snowman": { "category": "nature", "moji": "⛄", + "description": "snowman without snow", "unicodeVersion": "5.2", "digest": "6137456b2335e88e09c1859615eb22bb636355ef438f7a3949ad2f3d54478dd3" }, "snowman2": { "category": "nature", "moji": "☃", + "description": "snowman", "unicodeVersion": "1.1", "digest": "33ec75c22a13c81fa3c6b24a77ac1a08dc0dbe70b3716cf17b6702014d8a63fe" }, "sob": { "category": "people", "moji": "😭", + "description": "loudly crying face", "unicodeVersion": "6.0", "digest": "d1ed4b31861f9f9fd4e9c95a9c17530e2320a1b4cad6ececb1545ce25d65e4ce" }, "soccer": { "category": "activity", "moji": "⚽", + "description": "soccer ball", "unicodeVersion": "5.2", "digest": "6a3f2e6a9a0b64c3fbf8705995792091daf386a4112dba75507a1f556f662f84" }, "soon": { "category": "symbols", "moji": "🔜", + "description": "soon with rightwards arrow above", "unicodeVersion": "6.0", "digest": "a49d1bcfbac3e6ccc05b9a9863eff74b0eb8b4d4b22b8b0f7b2787fcba1c73cc" }, "sos": { "category": "symbols", "moji": "🆘", + "description": "squared sos", "unicodeVersion": "6.0", "digest": "2fa7e0274383aeed6019eb9177e778d7aab8b88575b078b0ffeb77cd18df14b3" }, "sound": { "category": "symbols", "moji": "🔉", + "description": "speaker with one sound wave", "unicodeVersion": "6.0", "digest": "faaca7b315b2495cbc381468580d25f1d11362441c35bb43d8a914f2ec8202d2" }, "space_invader": { "category": "activity", "moji": "👾", + "description": "alien monster", "unicodeVersion": "6.0", "digest": "e75379cb5063f9a8861d762ad1886097c1697fbb61f2e4e8f531047955a4a2dd" }, "spades": { "category": "symbols", "moji": "♠", + "description": "black spade suit", "unicodeVersion": "1.1", "digest": "2c4d20f6a4893cfc62498d3f1f8f67577f39ed09f3e6682d8cb9cd8f365d30da" }, "spaghetti": { "category": "food", "moji": "🍝", + "description": "spaghetti", "unicodeVersion": "6.0", "digest": "6d3451dc0faa1913539edb99261448f51735f269b61193c53dfe63466c0191e8" }, "sparkle": { "category": "symbols", "moji": "❇", + "description": "sparkle", "unicodeVersion": "1.1", "digest": "7131163cd6c2f879110c86e9f068c33cf580f7c4b619449c41851fe6083402ee" }, "sparkler": { "category": "travel", "moji": "🎇", + "description": "firework sparkler", "unicodeVersion": "6.0", "digest": "88539ed8a13bd66e0c265c0913bd3ec2ddc4d95484323595713beb102221a1f6" }, "sparkles": { "category": "nature", "moji": "✨", + "description": "sparkles", "unicodeVersion": "6.0", "digest": "cf84d16b1c0a381d5a7ae79031872747c9a6887eab6e92cc4a10a4b8600ef506" }, "sparkling_heart": { "category": "symbols", "moji": "💖", + "description": "sparkling heart", "unicodeVersion": "6.0", "digest": "b80b1ddef83b6528b309a194f6f2faf5acab603daeb9254523efc2b941bcb6d2" }, "speak_no_evil": { "category": "nature", "moji": "🙊", + "description": "speak-no-evil monkey", "unicodeVersion": "6.0", "digest": "d2d7cfb4d471928a496bdc146890adc8422a68500b68115630b24c125d18e81f" }, "speaker": { "category": "symbols", "moji": "🔈", + "description": "speaker", "unicodeVersion": "6.0", "digest": "dbca5f7181728d2ad67ff76fd566ffbdf53e333e7eeed341f54668bd47969413" }, "speaking_head": { "category": "people", "moji": "🗣", + "description": "speaking head in silhouette", "unicodeVersion": "7.0", "digest": "4be1af79b4506c00af4df64663413bcbae195dab0bc63c5011feb8f9663ed544" }, "speech_balloon": { "category": "symbols", "moji": "💬", + "description": "speech balloon", "unicodeVersion": "6.0", "digest": "817100d9979456e7d2f253ac22e13b7a2302dc1590566214915b003e403c53ca" }, "speedboat": { "category": "travel", "moji": "🚤", + "description": "speedboat", "unicodeVersion": "6.0", "digest": "a523b2320f0b24be1e9fdbc1ff828e28d8fd9a64d51e5888ab453ef0bc9f0576" }, "spider": { "category": "nature", "moji": "🕷", + "description": "spider", "unicodeVersion": "7.0", "digest": "8411eac0c1b80926fd93cc1d6423e00b05d04c485b79ee232da8f1714e899a37" }, "spider_web": { "category": "nature", "moji": "🕸", + "description": "spider web", "unicodeVersion": "7.0", "digest": "2434bdfbe56dcc4a43699dd59b638af431486b52fb1d6d685451f3b231b2be23" }, "spoon": { "category": "food", "moji": "🥄", + "description": "spoon", "unicodeVersion": "9.0", "digest": "4fa31d59e5bffd2c45a8e01fcd5652e78a5691cbfa744e69882bc67173ddea05" }, "spy": { "category": "people", "moji": "🕵", + "description": "sleuth or spy", "unicodeVersion": "7.0", "digest": "99fe3cdeff934726ee5855b0e401bf32570084aaad4eb10df837fd410ca742aa" }, "spy_tone1": { "category": "people", "moji": "🕵🏻", + "description": "sleuth or spy tone 1", "unicodeVersion": "8.0", "digest": "1720a99064061c43c7647b6bd517efa2ee2621b355a644adfb347d62849366a2" }, "spy_tone2": { "category": "people", "moji": "🕵🏼", + "description": "sleuth or spy tone 2", "unicodeVersion": "8.0", "digest": "23ff0026723f2b5a46fbfb55e24c4a4a33af2bd96808b3ea3af76aae99965d68" }, "spy_tone3": { "category": "people", "moji": "🕵🏽", + "description": "sleuth or spy tone 3", "unicodeVersion": "8.0", "digest": "1d0cb3d54fb61e4763a4f0642ef32094bdd40832be0d42799ce9ba69773616df" }, "spy_tone4": { "category": "people", "moji": "🕵🏾", + "description": "sleuth or spy tone 4", "unicodeVersion": "8.0", "digest": "e36a4b52df6cb954fab9d9128111f1301c6d46bdeacf51993ffb5bb354cd0ad3" }, "spy_tone5": { "category": "people", "moji": "🕵🏿", + "description": "sleuth or spy tone 5", "unicodeVersion": "8.0", "digest": "ffc6fefd9a537124ebf0a9ddf387414dce1291335026064644f6cf9315591129" }, "squid": { "category": "nature", "moji": "🦑", + "description": "squid", "unicodeVersion": "9.0", "digest": "65a1b318c2c506b9d26cfd8282a5cf9922109595c8d12e92c3f7481ac7c08c49" }, "stadium": { "category": "travel", "moji": "🏟", + "description": "stadium", "unicodeVersion": "7.0", "digest": "73bf955e767ba1518c9c92b2ba59a2aa1ec4b018652dffd97bcd74832a33789f" }, "star": { "category": "nature", "moji": "⭐", + "description": "white medium star", "unicodeVersion": "5.1", "digest": "d78e5c1b78caed103e100150c10b08a9ca3ee30c243943d6fc3cc08f422122e9" }, "star2": { "category": "nature", "moji": "🌟", + "description": "glowing star", "unicodeVersion": "6.0", "digest": "f91ac4afe3f5d4a52847ae8b4a9704b591e00399aebba553d150d7e34ee939fa" }, "star_and_crescent": { "category": "symbols", "moji": "☪", + "description": "star and crescent", "unicodeVersion": "1.1", "digest": "1bf3d29e50034f5e7c0dccff0a3a533b74bfa9b489e357b2739a473311f1332a" }, "star_of_david": { "category": "symbols", "moji": "✡", + "description": "star of david", "unicodeVersion": "1.1", "digest": "28a0bd0eeac9d0835ceb8425d72c2472464e863dd09b76a0ddc1c08cf1986402" }, "stars": { "category": "travel", "moji": "🌠", + "description": "shooting star", "unicodeVersion": "6.0", "digest": "837d9045316b8fb5e533457eac61241534f641eb78d8cb75f688f80fb8e8a7f0" }, "station": { "category": "travel", "moji": "🚉", + "description": "station", "unicodeVersion": "6.0", "digest": "27a163ac0aea4ed247a121cae826eafc475977c68b0d888e9405bea14326ff56" }, "statue_of_liberty": { "category": "travel", "moji": "🗽", + "description": "statue of liberty", "unicodeVersion": "6.0", "digest": "f5a43599ab3f24ed3a78a745e06e2ac3e33107a292386ad81c67935ee5b22493" }, "steam_locomotive": { "category": "travel", "moji": "🚂", + "description": "steam locomotive", "unicodeVersion": "6.0", "digest": "52ad0073f37b978faf3884fb193046f2b0614e1557bbcc9de1b020e42aff2dba" }, "stew": { "category": "food", "moji": "🍲", + "description": "pot of food", "unicodeVersion": "6.0", "digest": "c16f61236db314ad8d9f2dd241ec1e15c8d64e5872cce93ec4d0996490dd39df" }, "stop_button": { "category": "symbols", "moji": "⏹", + "description": "black square for stop", "unicodeVersion": "7.0", "digest": "83f9d0da3ad845fef41b4e8336815d30e9c8f042ab2a8340894ade2f428fc98a" }, "stopwatch": { "category": "objects", "moji": "⏱", + "description": "stopwatch", "unicodeVersion": "6.0", "digest": "9b6b9491a24d8ab4f896eb876da7973f028bd5e7c51a3767ba7e61bb6fbb2be0" }, "straight_ruler": { "category": "objects", "moji": "📏", + "description": "straight ruler", "unicodeVersion": "6.0", "digest": "cee31101767bd3f961363599924dc3790675d05a1285a8396428d2f91771c111" }, "strawberry": { "category": "food", "moji": "🍓", + "description": "strawberry", "unicodeVersion": "6.0", "digest": "5750a15e12f21259286ddbc3a8222a385b3b97a9f368897f42dd000060343174" }, "stuck_out_tongue": { "category": "people", "moji": "😛", + "description": "face with stuck-out tongue", "unicodeVersion": "6.1", "digest": "92dc42980a6dfdd7204fc874a762d6a0bbf0fdbfb5a7c0698fca04782e99fde6" }, "stuck_out_tongue_closed_eyes": { "category": "people", "moji": "😝", + "description": "face with stuck-out tongue and tightly-closed eyes", "unicodeVersion": "6.0", "digest": "434d25ac24cad7ba699eae876a25d9a99b584449cca50b124bf6aa7f20a83d51" }, "stuck_out_tongue_winking_eye": { "category": "people", "moji": "😜", + "description": "face with stuck-out tongue and winking eye", "unicodeVersion": "6.0", "digest": "dbacd6428a2a2933212e6a4dc0c7f302177fb23b963626ccb26f27f91737f03d" }, "stuffed_flatbread": { "category": "food", "moji": "🥙", + "description": "stuffed flatbread", "unicodeVersion": "9.0", "digest": "9f841f2520640d69be4f20a3199023d5811842b28556b5e1152e5ec11f0fda07" }, "sun_with_face": { "category": "nature", "moji": "🌞", + "description": "sun with face", "unicodeVersion": "6.0", "digest": "7256ff5263006c64c03f1eb66e3ddb56d67d785d65dacc37aa886d0cd4be63be" }, "sunflower": { "category": "nature", "moji": "🌻", + "description": "sunflower", "unicodeVersion": "6.0", "digest": "27d1161f50f932a6b26c404cf2e8f7083683ed0f2382d62b7472acccaa6eb695" }, "sunglasses": { "category": "people", "moji": "😎", + "description": "smiling face with sunglasses", "unicodeVersion": "6.0", "digest": "966684382e5c59e98319e4c0ea7c304c61c2638ad5408faa49ce2c83c4416757" }, "sunny": { "category": "nature", "moji": "☀", + "description": "black sun with rays", "unicodeVersion": "1.1", "digest": "460fea4cbbdd1595450c1033a2ee5de7fea2e2f147822efa49f7e204812415aa" }, "sunrise": { "category": "travel", "moji": "🌅", + "description": "sunrise", "unicodeVersion": "6.0", "digest": "7718a49636b0cdd1862ed67c7a9d6e72f471c2591ff0d912485b1be55d1ea115" }, "sunrise_over_mountains": { "category": "travel", "moji": "🌄", + "description": "sunrise over mountains", "unicodeVersion": "6.0", "digest": "743d0701cdbe2a814962363813c3153d3c5e62c3e410349f56d49dbb9581f356" }, "surfer": { "category": "activity", "moji": "🏄", + "description": "surfer", "unicodeVersion": "6.0", "digest": "bb440775e9213430942015c37db8de58b5a561ee971b2a0f3993fc3f1d2554d4" }, "surfer_tone1": { "category": "activity", "moji": "🏄🏻", + "description": "surfer tone 1", "unicodeVersion": "8.0", "digest": "a4937b030aca30b68bb644f37cf63c38aebce3c00b57d1c8a0ffe596b57d2f1e" }, "surfer_tone2": { "category": "activity", "moji": "🏄🏼", + "description": "surfer tone 2", "unicodeVersion": "8.0", "digest": "1c2a954a9c5284dedf0327d6f3c954c9fdd3953b848076d298874775ad8bf0a3" }, "surfer_tone3": { "category": "activity", "moji": "🏄🏽", + "description": "surfer tone 3", "unicodeVersion": "8.0", "digest": "418a3408b9ab026124f067c8597b500217e56bc28d9844a29eea5eee6f604ff8" }, "surfer_tone4": { "category": "activity", "moji": "🏄🏾", + "description": "surfer tone 4", "unicodeVersion": "8.0", "digest": "530870b9ac9f4d45ff750e264feb90b44fb93ca2852f323987b06f5f12fb5a4d" }, "surfer_tone5": { "category": "activity", "moji": "🏄🏿", + "description": "surfer tone 5", "unicodeVersion": "8.0", "digest": "40e11b1ae652cfd085d083377f1da24160065ed1b67403c6fa4655e6e44169ec" }, "sushi": { "category": "food", "moji": "🍣", + "description": "sushi", "unicodeVersion": "6.0", "digest": "b924c621236ca3284b349b0509ae1043f2fc2c7f6d67615716f9717ada78c992" }, "suspension_railway": { "category": "travel", "moji": "🚟", + "description": "suspension railway", "unicodeVersion": "6.0", "digest": "cd3d21da79864f0c018b863e82fb0561fff3c5e3c065303cfcb89c3663d638ba" }, "sweat": { "category": "people", "moji": "😓", + "description": "face with cold sweat", "unicodeVersion": "6.0", "digest": "1aa771479aa1ac5eeea4bafbe93ebd85a0f692f6d869034f31e25b689c2e264d" }, "sweat_drops": { "category": "nature", "moji": "💦", + "description": "splashing sweat symbol", "unicodeVersion": "6.0", "digest": "b575b85415bc9852cf6415d417ebf799167fde03c6819ebcaa24ae1b3dde8dab" }, "sweat_smile": { "category": "people", "moji": "😅", + "description": "smiling face with open mouth and cold sweat", "unicodeVersion": "6.0", "digest": "171b0d0845d46c33bedb6d3b39fb1ff366e22ba90685eedabebd91bb2b0680de" }, "sweet_potato": { "category": "food", "moji": "🍠", + "description": "roasted sweet potato", "unicodeVersion": "6.0", "digest": "4b91920f0b87d42763313bc476f4c821a74e4c12dc1c92165a859dddeaaf8844" }, "swimmer": { "category": "activity", "moji": "🏊", + "description": "swimmer", "unicodeVersion": "6.0", "digest": "2c4ed4a51aad99d9957ae11a219d5164db9748fc3a65002c6085a9f15adfa9e2" }, "swimmer_tone1": { "category": "activity", "moji": "🏊🏻", + "description": "swimmer tone 1", "unicodeVersion": "8.0", "digest": "48588f129ee4af52ca2e0f4594213391978601087cd607896b2f979ca077284b" }, "swimmer_tone2": { "category": "activity", "moji": "🏊🏼", + "description": "swimmer tone 2", "unicodeVersion": "8.0", "digest": "fff209448524bd1ef4d6decabf6c1ead94c8d3d5b1bfb5e54f20cc8e139232fc" }, "swimmer_tone3": { "category": "activity", "moji": "🏊🏽", + "description": "swimmer tone 3", "unicodeVersion": "8.0", "digest": "2003932cb2cf4ae9a10b23338bf375a9293fb18c0ecf91bdfae73be6eebb3800" }, "swimmer_tone4": { "category": "activity", "moji": "🏊🏾", + "description": "swimmer tone 4", "unicodeVersion": "8.0", "digest": "20b4bff9baa1c694ad98067dde834c56092f023b9664bec382c2e512232bd480" }, "swimmer_tone5": { "category": "activity", "moji": "🏊🏿", + "description": "swimmer tone 5", "unicodeVersion": "8.0", "digest": "0ff8eb57c2be8e80a1bc6ba75b8d9ffb9bd8d3be636150c4c03399ec1886f218" }, "symbols": { "category": "symbols", "moji": "🔣", + "description": "input symbol for symbols", "unicodeVersion": "6.0", "digest": "2a2a79816c4d0751a0d73586eec5e63b410653d3c85cc968906bf1fc03d89b94" }, "synagogue": { "category": "travel", "moji": "🕍", + "description": "synagogue", "unicodeVersion": "8.0", "digest": "98569cdd7c61528963b67b7891dfa46025c5e810cbb22ee18ddb3bd85de2da69" }, "syringe": { "category": "objects", "moji": "💉", + "description": "syringe", "unicodeVersion": "6.0", "digest": "e1538e645ccc571227c994b71b3d1be2c4d072d8bd9c944a42ff4a11c91a34a6" }, "taco": { "category": "food", "moji": "🌮", + "description": "taco", "unicodeVersion": "8.0", "digest": "e1e45aefdb7445faeae75c3831df6a3d6f2590fcdd48a20d847593c246df613b" }, "tada": { "category": "objects", "moji": "🎉", + "description": "party popper", "unicodeVersion": "6.0", "digest": "1d2e6cbb2a3244240bc70209715d2213d1efee2e370cccfbcc046c333ae2d650" }, "tanabata_tree": { "category": "nature", "moji": "🎋", + "description": "tanabata tree", "unicodeVersion": "6.0", "digest": "592f2907ffc1b914390e1a106c15120ff3607e99192158b94d237975647c5540" }, "tangerine": { "category": "food", "moji": "🍊", + "description": "tangerine", "unicodeVersion": "6.0", "digest": "40c9ddcde1b0bcfaeb466629a87825eb8c2037835720cbee5e2fda04be3c8d0a" }, "taurus": { "category": "symbols", "moji": "♉", + "description": "taurus", "unicodeVersion": "1.1", "digest": "21cf24cb6410ab6596e2df8b3e242cc07f9dbb247eabc00c590fe184b373d068" }, "taxi": { "category": "travel", "moji": "🚕", + "description": "taxi", "unicodeVersion": "6.0", "digest": "c546cc743831cfbf0c15452767cf2a4faf3775066797e997ae7c1fcbe4eca479" }, "tea": { "category": "food", "moji": "🍵", + "description": "teacup without handle", "unicodeVersion": "6.0", "digest": "00e3f1e389fa58c4fcd8c53ebbf83d25872f4315845ab1984b35410ae65553d9" }, "telephone": { "category": "objects", "moji": "☎", + "description": "black telephone", "unicodeVersion": "1.1", "digest": "3a53851e641f8ad938ce3597b1afca2ea63c9314ff81f62563b99937496a13d7" }, "telephone_receiver": { "category": "objects", "moji": "📞", + "description": "telephone receiver", "unicodeVersion": "6.0", "digest": "1614d67f3d8814b0d75f39d55f9149e4d28ef57b343498625e62fcfff8365046" }, "telescope": { "category": "objects", "moji": "🔭", + "description": "telescope", "unicodeVersion": "6.0", "digest": "4adf40387870276c4f59fb050d441023e8dac784365b6a8c0282fb519780b495" }, "ten": { "category": "symbols", "moji": "🔟", + "description": "keycap ten", "unicodeVersion": "6.0", "digest": "c7c9491021740d2c17edddb856f79579b0b943d8dc85a2f48dbaac84f35b8a40" }, "tennis": { "category": "activity", "moji": "🎾", + "description": "tennis racquet and ball", "unicodeVersion": "6.0", "digest": "dc1600b4d8dce3d26259eb0d1c6ab042566565e3c1f2c96112210f1550a716fd" }, "tent": { "category": "travel", "moji": "⛺", + "description": "tent", "unicodeVersion": "5.2", "digest": "30d9b17ac3219d4970ddf54d7c1a288b0ae50f7f3b82ed232c0b1b19ef585662" }, "thermometer": { "category": "objects", "moji": "🌡", + "description": "thermometer", "unicodeVersion": "7.0", "digest": "66616babbcaef256d7b652796c760e8e893cb950c073348a408fe70904f80f25" }, "thermometer_face": { "category": "people", "moji": "🤒", + "description": "face with thermometer", "unicodeVersion": "8.0", "digest": "ac2b5caddd128563711a9dcc7f690cf210f684d5e8b64b09c0431d6902437126" }, "thinking": { "category": "people", "moji": "🤔", + "description": "thinking face", "unicodeVersion": "8.0", "digest": "4f0b84e5ab8a650cafb166e93688f0e9b31b9ade22a91035261ac90490edb9d3" }, "third_place": { "category": "activity", "moji": "🥉", + "description": "third place medal", "unicodeVersion": "9.0", "digest": "27c9bcba44ad95bee30882cc0722e8b0a798206306655dd648e884447ed26808" }, "thought_balloon": { "category": "symbols", "moji": "💭", + "description": "thought balloon", "unicodeVersion": "6.0", "digest": "bf59624560c333561d636aedf2c8827089e275895cf434974daaabb3d5cea46e" }, "three": { "category": "symbols", "moji": "3️⃣", + "description": "keycap digit three", "unicodeVersion": "3.0", "digest": "d3f85828787799c769655c38a519cad0743ab799ab276c7606e6e6894cc442e6" }, "thumbsdown": { "category": "people", "moji": "👎", + "description": "thumbs down sign", "unicodeVersion": "6.0", "digest": "5954334e2dae5357312b3d629f10a496c728029e02216f8c8b887f9b51561c61" }, "thumbsdown_tone1": { "category": "people", "moji": "👎🏻", + "description": "thumbs down sign tone 1", "unicodeVersion": "8.0", "digest": "3c2853491473fd7ae2d1b5415a425cc390d26a8754446f8736c1360e4cb18ba3" }, "thumbsdown_tone2": { "category": "people", "moji": "👎🏼", + "description": "thumbs down sign tone 2", "unicodeVersion": "8.0", "digest": "4e0f8f86a06b69e423df8d93f41ec393f12800633acc82c4cb6dff64ca0d8507" }, "thumbsdown_tone3": { "category": "people", "moji": "👎🏽", + "description": "thumbs down sign tone 3", "unicodeVersion": "8.0", "digest": "e08fa35575f59978612d4330bbc35313eca9c4dfa04f4212626abc700819effe" }, "thumbsdown_tone4": { "category": "people", "moji": "👎🏾", + "description": "thumbs down sign tone 4", "unicodeVersion": "8.0", "digest": "7c6d118d20d5add8ca003e4a53e42685a1f9436b872ed10d79f67ad418fb2a44" }, "thumbsdown_tone5": { "category": "people", "moji": "👎🏿", + "description": "thumbs down sign tone 5", "unicodeVersion": "8.0", "digest": "8697c4a4ee4d6669dc2d47aa97699c42012ca59b80818ad6845878b37b4a9c58" }, "thumbsup": { "category": "people", "moji": "👍", + "description": "thumbs up sign", "unicodeVersion": "6.0", "digest": "59ec2457ab33e8897261d01a495f6cf5c668d0004807dc541c3b1be5294b1e61" }, "thumbsup_tone1": { "category": "people", "moji": "👍🏻", + "description": "thumbs up sign tone 1", "unicodeVersion": "8.0", "digest": "f57e6c525e8830779ea5026590eec3ca10869dc438a0c779734b617d04f28d21" }, "thumbsup_tone2": { "category": "people", "moji": "👍🏼", + "description": "thumbs up sign tone 2", "unicodeVersion": "8.0", "digest": "980eeeb1d8f5d79dae35c7ff81a576e980aa13a440d07b10e32e98ed34cbf7f1" }, "thumbsup_tone3": { "category": "people", "moji": "👍🏽", + "description": "thumbs up sign tone 3", "unicodeVersion": "8.0", "digest": "b3881060569e56e1dd75ca7960feab0e58ae51f440458781948d65d461116b4e" }, "thumbsup_tone4": { "category": "people", "moji": "👍🏾", + "description": "thumbs up sign tone 4", "unicodeVersion": "8.0", "digest": "86fbe2c95414bce5e38fb5c33da31305d7942fca2c9c79168dcffdbd895e9ad6" }, "thumbsup_tone5": { "category": "people", "moji": "👍🏿", + "description": "thumbs up sign tone 5", "unicodeVersion": "8.0", "digest": "49fa63ff725c746a18649df16c8fab69bad88bbb564884df79d1d15f553b7343" }, "thunder_cloud_rain": { "category": "nature", "moji": "⛈", + "description": "thunder cloud and rain", "unicodeVersion": "5.2", "digest": "dacc20b4f6b68e5834aa1b8391afa5e83b5e6eb28e2d2174d3a68186a770506d" }, "ticket": { "category": "activity", "moji": "🎫", + "description": "ticket", "unicodeVersion": "6.0", "digest": "b4326fe7761940216e6c76ee2928110a6b37bf913da9d694e96557e7c7c10420" }, "tickets": { "category": "activity", "moji": "🎟", + "description": "admission tickets", "unicodeVersion": "7.0", "digest": "fb73358c3697c04fcfde6a1e705b1c3b47635b93b9cadfe31d5657566c7d190a" }, "tiger": { "category": "nature", "moji": "🐯", + "description": "tiger face", "unicodeVersion": "6.0", "digest": "e139531e6c930bc46242dc0ed274661229de026b5419d8ea8f99fdb0f8a719ab" }, "tiger2": { "category": "nature", "moji": "🐅", + "description": "tiger", "unicodeVersion": "6.0", "digest": "f930cc8714198310d9b0edca6baff243ac5a3320f75fadb56fa5acc6fe34ff24" }, "timer": { "category": "objects", "moji": "⏲", + "description": "timer clock", "unicodeVersion": "6.0", "digest": "69b33f219523d89d81cbbc070ad7e528711e4b34e124a50acb12a0280a34d0b0" }, "tired_face": { "category": "people", "moji": "😫", + "description": "tired face", "unicodeVersion": "6.0", "digest": "775739bc9324517e614878ca0960d793df97775feeb62b14dbfb311a42a21802" }, "tm": { "category": "symbols", "moji": "™", + "description": "trade mark sign", "unicodeVersion": "1.1", "digest": "7d9fafdb72d91860478fc185719f289f359eab2c368a132cb936a269e2ab6a24" }, "toilet": { "category": "objects", "moji": "🚽", + "description": "toilet", "unicodeVersion": "6.0", "digest": "0d1b0dd0078f51104e8632a0726e1b3f075561a1ffa8a2546602de15798415d0" }, "tokyo_tower": { "category": "travel", "moji": "🗼", + "description": "tokyo tower", "unicodeVersion": "6.0", "digest": "73eaf6fd59d16396673afef620c6d928857d5cf616e95a40eaf2861686e0956a" }, "tomato": { "category": "food", "moji": "🍅", + "description": "tomato", "unicodeVersion": "6.0", "digest": "d092d8ad381d542e59b6a82b4f1ef0d10fc1ed48460952375c6c5c6258cea111" }, "tone1": { "category": "modifier", "moji": "🏻", + "description": "emoji modifier Fitzpatrick type-1-2", "unicodeVersion": "8.0", "digest": "5c62003a098b774c068be45d658db3c0dd38483c0871f7c8ae293bc1222c4f0c" }, "tone2": { "category": "modifier", "moji": "🏼", + "description": "emoji modifier Fitzpatrick type-3", "unicodeVersion": "8.0", "digest": "3c636ecbc4e58c7a360f2338daaf44e7da598fd07e0ba1514bb5c0f83fc8819f" }, "tone3": { "category": "modifier", "moji": "🏽", + "description": "emoji modifier Fitzpatrick type-4", "unicodeVersion": "8.0", "digest": "398a1e5441b64c9c2d033bbc01d7a8d90b4db30ea9f30e28f0a9120c72a48df8" }, "tone4": { "category": "modifier", "moji": "🏾", + "description": "emoji modifier Fitzpatrick type-5", "unicodeVersion": "8.0", "digest": "ff4a12195aeb7494c785b81266efad8cd60c8022c407a0fc032a02e8b83216b3" }, "tone5": { "category": "modifier", "moji": "🏿", + "description": "emoji modifier Fitzpatrick type-6", "unicodeVersion": "8.0", "digest": "9e9f0125b5d57011b7456c84719e6be6cf71d06c1b198081d0937c0979164a81" }, "tongue": { "category": "people", "moji": "👅", + "description": "tongue", "unicodeVersion": "6.0", "digest": "286e9d2583c371431d6fc979dd4ab48981676da26baada51a846657a3654c19b" }, "tools": { "category": "objects", "moji": "🛠", + "description": "hammer and wrench", "unicodeVersion": "7.0", "digest": "bf08d60dedc06de73d04dab05703bb8ad81989c72b5035d1a07821e51096f158" }, "top": { "category": "symbols", "moji": "🔝", + "description": "top with upwards arrow above", "unicodeVersion": "6.0", "digest": "c9a9f25b17db014e76b6be54aa07ef89bb18f8adb41b3199d180a559ff1d9ea5" }, "tophat": { "category": "people", "moji": "🎩", + "description": "top hat", "unicodeVersion": "6.0", "digest": "43a45dfb5d6b57a63a0491f4e3ec780774c0301b53ed39a303a0bd803d16ed71" }, "track_next": { "category": "symbols", "moji": "⏭", + "description": "black right-pointing double triangle with vertical bar", "unicodeVersion": "6.0", "digest": "88592ef6c720a32aeb752322fb4c794bf5110a72408e21e898630452115c731c" }, "track_previous": { "category": "symbols", "moji": "⏮", + "description": "black left-pointing double triangle with vertical bar", "unicodeVersion": "6.0", "digest": "98c1b3d643768d94857fb762f6d26cfb87282b449a67792242e8b7068643ac87" }, "trackball": { "category": "objects", "moji": "🖲", + "description": "trackball", "unicodeVersion": "7.0", "digest": "32a819a3129429f797ad434d0c40e263dc236808e34878c599ed2304b43702f5" }, "tractor": { "category": "travel", "moji": "🚜", + "description": "tractor", "unicodeVersion": "6.0", "digest": "5e4686290f1a4c9953ae208340b7d276f25b3b2197a43e52469aeb6450e93997" }, "traffic_light": { "category": "travel", "moji": "🚥", + "description": "horizontal traffic light", "unicodeVersion": "6.0", "digest": "d96aacade33d1ad3e0414f8a920513010f36eb7e5889774251c1d91148917ead" }, "train": { "category": "travel", "moji": "🚋", + "description": "Tram Car", "unicodeVersion": "6.0", "digest": "7423d17e131df7aadaa350b5d39dcbce3b28de331ff8b6703a3b2d0093963f4b" }, "train2": { "category": "travel", "moji": "🚆", + "description": "train", "unicodeVersion": "6.0", "digest": "06e65d549e771632f3c64287a38ba67236f9800ccb6a23c3b592bc010e24e122" }, "tram": { "category": "travel", "moji": "🚊", + "description": "tram", "unicodeVersion": "6.0", "digest": "21a7699f1a94f06dcb4d1e896448b98a4205f8efe902a8ac169a5005d11ab100" }, "triangular_flag_on_post": { "category": "objects", "moji": "🚩", + "description": "triangular flag on post", "unicodeVersion": "6.0", "digest": "1f5ce3828a42f5b1717bac1521d0502cf7081ad9f15e8ed292c1a65f0d1386da" }, "triangular_ruler": { "category": "objects", "moji": "📐", + "description": "triangular ruler", "unicodeVersion": "6.0", "digest": "a0367dcf663ec934f1fc7c88bfaccc02b229a896f60930a66bb02241c933e501" }, "trident": { "category": "symbols", "moji": "🔱", + "description": "trident emblem", "unicodeVersion": "6.0", "digest": "ee45920845d3b35c2e45b934cf30ce97bfe2f24c5d72ef1ac6e0842e52b50fc1" }, "triumph": { "category": "people", "moji": "😤", + "description": "face with look of triumph", "unicodeVersion": "6.0", "digest": "4aa44b8e1682c1269624a359f4b0bf613553683b883d947561ab169d7f85da0f" }, "trolleybus": { "category": "travel", "moji": "🚎", + "description": "trolleybus", "unicodeVersion": "6.0", "digest": "f610b4fd1123f06778a8e3bb8f738d5b0079aeb0b0926b6a63268c0dd0ee03ed" }, "trophy": { "category": "activity", "moji": "🏆", + "description": "trophy", "unicodeVersion": "6.0", "digest": "50cfbedac18bf0fa5dec727643e15ec47f64068944b536e97518ee3be4f08006" }, "tropical_drink": { "category": "food", "moji": "🍹", + "description": "tropical drink", "unicodeVersion": "6.0", "digest": "54144fce60d650f426b1edf09e47c70b2762222398c1fe40231881f074603a69" }, "tropical_fish": { "category": "nature", "moji": "🐠", + "description": "tropical fish", "unicodeVersion": "6.0", "digest": "fd92100aaa9328da35e6090388824921b9726b474d1432a926d2cf9c45ad6528" }, "truck": { "category": "travel", "moji": "🚚", + "description": "delivery truck", "unicodeVersion": "6.0", "digest": "0d1571e58e900abc453df0ff683fe7acb5906ecbdd52ab35b7101074359faf18" }, "trumpet": { "category": "activity", "moji": "🎺", + "description": "trumpet", "unicodeVersion": "6.0", "digest": "cea3614c309f5573f328f4603120dbe930016a35f0dfa400b0d968fe9fff2d55" }, "tulip": { "category": "nature", "moji": "🌷", + "description": "tulip", "unicodeVersion": "6.0", "digest": "e744e8dbbdc6b126bd5b15aad56b524191de5a604189f4ab6d96730dfef4d086" }, "tumbler_glass": { "category": "food", "moji": "🥃", + "description": "tumbler glass", "unicodeVersion": "9.0", "digest": "7a38658274b9ff28836725a1dbfad49b8fa3af5ec8385e629db6bfdc7d93907a" }, "turkey": { "category": "nature", "moji": "🦃", + "description": "turkey", "unicodeVersion": "8.0", "digest": "bf5daef15716b66636a5fdb6d059420521443c0603e2d56bd7c99c791a7285f4" }, "turtle": { "category": "nature", "moji": "🐢", + "description": "turtle", "unicodeVersion": "6.0", "digest": "588c35fb42c9502a908e9805517d4cc8c4ba4e74c9beed4035779fea1efe14f8" }, "tv": { "category": "objects", "moji": "📺", + "description": "television", "unicodeVersion": "6.0", "digest": "1279f3f3955a58dbbf74e248fc914b0bdba9c4c6b6a5176e9d12bf2750ecfeb4" }, "twisted_rightwards_arrows": { "category": "symbols", "moji": "🔀", + "description": "twisted rightwards arrows", "unicodeVersion": "6.0", "digest": "fed07eebc2cf0d977ca0826bbd80defafbbcf118508444148f47b58949ebe27c" }, "two": { "category": "symbols", "moji": "2️⃣", + "description": "keycap digit two", "unicodeVersion": "3.0", "digest": "b346f51f6523b02ebcbd753256804e2f9cc1574c96aa634362bf9401dac2c661" }, "two_hearts": { "category": "symbols", "moji": "💕", + "description": "two hearts", "unicodeVersion": "6.0", "digest": "6ded120a59aed790b441ec8fbbdea6f5cbfb4fa48e9e4b224cc29c9fde2d2e4c" }, "two_men_holding_hands": { "category": "people", "moji": "👬", + "description": "two men holding hands", "unicodeVersion": "6.0", "digest": "bfcf9e20a67d00262cdf6e85f1acd545dda91f2e370d68bfd41ce02f232a2987" }, "two_women_holding_hands": { "category": "people", "moji": "👭", + "description": "two women holding hands", "unicodeVersion": "6.0", "digest": "9d9d2b37a7f8e16fde1468dd8b5645003ea81ae4bf8bcf68471e2381845dd0dd" }, "u5272": { "category": "symbols", "moji": "🈹", + "description": "squared cjk unified ideograph-5272", "unicodeVersion": "6.0", "digest": "01e6cb8f74ea3c19fdade59c2d13d158b90dc6b4b293421b2014b7478bf20870" }, "u5408": { "category": "symbols", "moji": "🈴", + "description": "squared cjk unified ideograph-5408", "unicodeVersion": "6.0", "digest": "084cdbd5436670ea4dc22010e269c1ab7b0432897b8675301e69120374bcdd14" }, "u55b6": { "category": "symbols", "moji": "🈺", + "description": "squared cjk unified ideograph-55b6", "unicodeVersion": "6.0", "digest": "c1017023d20d4aae78d59342dd3bfc5282716ea0601d9a8c2476335cbf7a2e12" }, "u6307": { "category": "symbols", "moji": "🈯", + "description": "squared cjk unified ideograph-6307", "unicodeVersion": "5.2", "digest": "f459b092b974f459db1fb9cc13617a448b2e4f2b4dc46cc316d8c46af6e7d8bd" }, "u6708": { "category": "symbols", "moji": "🈷", + "description": "squared cjk unified ideograph-6708", "unicodeVersion": "6.0", "digest": "928815abf5b30f92efe5168de0c7e6cf8c17899a03e358ab42f42667e0a4a04c" }, "u6709": { "category": "symbols", "moji": "🈶", + "description": "squared cjk unified ideograph-6709", "unicodeVersion": "6.0", "digest": "f63a48ee06c892d24acec8b5634c021658d2ebde67a42d8faa86f27804a9f26d" }, "u6e80": { "category": "symbols", "moji": "🈵", + "description": "squared cjk unified ideograph-6e80", "unicodeVersion": "6.0", "digest": "489181d90a5e43068459530673a153e4af04fdad8514ec341ff7afbcfd366c3b" }, "u7121": { "category": "symbols", "moji": "🈚", + "description": "squared cjk unified ideograph-7121", "unicodeVersion": "5.2", "digest": "9c50fd2ba14221affd2dcd3746322c2137dd75458493f4d385b544eb5bd8d6cd" }, "u7533": { "category": "symbols", "moji": "🈸", + "description": "squared cjk unified ideograph-7533", "unicodeVersion": "6.0", "digest": "2b05819b380a2ea47cc5fde8fcce3d53922fd223d6f5bd83d696d44175b69f18" }, "u7981": { "category": "symbols", "moji": "🈲", + "description": "squared cjk unified ideograph-7981", "unicodeVersion": "6.0", "digest": "adbe12601b22972003ddebcb0bd1532b979aa9c78bfdc147511854b5014eabc0" }, "u7a7a": { "category": "symbols", "moji": "🈳", + "description": "squared cjk unified ideograph-7a7a", "unicodeVersion": "6.0", "digest": "b9ee0ec7bb0b86c3eb73d4dbbb91848c427bf356ae30a263b9b44bd9bd784482" }, "umbrella": { "category": "nature", "moji": "☔", + "description": "umbrella with rain drops", "unicodeVersion": "4.0", "digest": "0328a2f48b7df47905e2655460e524c0794ef12d3d7c32a049a10892d5662f77" }, "umbrella2": { "category": "nature", "moji": "☂", + "description": "umbrella", "unicodeVersion": "1.1", "digest": "2f6a58110dc590480a822a3ffa2b5bc86f295e0c994a4a632837d25d4cf9fc58" }, "unamused": { "category": "people", "moji": "😒", + "description": "unamused face", "unicodeVersion": "6.0", "digest": "0d597088e3e7880918d0166e5c69243b18fe64afa31685c39bfdbc71494aa132" }, "underage": { "category": "symbols", "moji": "🔞", + "description": "no one under eighteen symbol", "unicodeVersion": "6.0", "digest": "b6b194614ca714ac2b1c2c17b75fe5922c7fdadb3d1157ba89ab2a5d03494a67" }, "unicorn": { "category": "nature", "moji": "🦄", + "description": "unicorn face", "unicodeVersion": "8.0", "digest": "f71bb485a7c208e999dd45f2b36d7b7d517898c0627947926b05aa28603804ca" }, "unlock": { "category": "objects", "moji": "🔓", + "description": "open lock", "unicodeVersion": "6.0", "digest": "9554ef3a6a315938b873e77970d9b0212e61f13c6cc36e4f17f87acc930a9a53" }, "up": { "category": "symbols", "moji": "🆙", + "description": "squared up with exclamation mark", "unicodeVersion": "6.0", "digest": "ff2554ccf08c7208b38794c5fa3d9a93a46ff191a49401195d8f740846121906" }, "upside_down": { "category": "people", "moji": "🙃", + "description": "upside-down face", "unicodeVersion": "8.0", "digest": "5129121f0a28f5b334268c28565de26a5907559568deca11de6ec620b097dfe1" }, "urn": { "category": "objects", "moji": "⚱", + "description": "funeral urn", "unicodeVersion": "4.1", "digest": "9bebf589eed8dd361f6a03cd1b325078f2cd0e82270ef63a7dd1b6aee08cd1e6" }, "v": { "category": "people", "moji": "✌", + "description": "victory hand", "unicodeVersion": "1.1", "digest": "9825bf440df289a8edf8ede494e8c778dc63c95f967f4d7bbea3245cf4f558ec" }, "v_tone1": { "category": "people", "moji": "✌🏻", + "description": "victory hand tone 1", "unicodeVersion": "8.0", "digest": "76e358250d9ca519b60b8d7b6a32900700d784433dcc609e9442254a410f6e37" }, "v_tone2": { "category": "people", "moji": "✌🏼", + "description": "victory hand tone 2", "unicodeVersion": "8.0", "digest": "4081b674be8416136022523fa9f29ec70a0f7e3aa05ca13152606609f3fd003c" }, "v_tone3": { "category": "people", "moji": "✌🏽", + "description": "victory hand tone 3", "unicodeVersion": "8.0", "digest": "b6afb3a4c78384280610b953592d378241c75597a82aa6d16c86a993f8d8f3b0" }, "v_tone4": { "category": "people", "moji": "✌🏾", + "description": "victory hand tone 4", "unicodeVersion": "8.0", "digest": "7ddc3cdd0138da2c8d7f6d8257ffdb8801496043e8a2395f93b0663447ac7fce" }, "v_tone5": { "category": "people", "moji": "✌🏿", + "description": "victory hand tone 5", "unicodeVersion": "8.0", "digest": "a85dc5c589f0d1cf32f8bfa5c82e5c11c40b35439636914686a2f06f7359f539" }, "vertical_traffic_light": { "category": "travel", "moji": "🚦", + "description": "vertical traffic light", "unicodeVersion": "6.0", "digest": "8cfd49a8f96b15a8313ef855f2e234ea3fa58332e68896dea34760740de9f020" }, "vhs": { "category": "objects", "moji": "📼", + "description": "videocassette", "unicodeVersion": "6.0", "digest": "3fb1acaf25805cf86f8d40ee2c17cf25da587b7ca93b931167ab43fce041eee8" }, "vibration_mode": { "category": "symbols", "moji": "📳", + "description": "vibration mode", "unicodeVersion": "6.0", "digest": "c9a8899222f46fe51dd8cee3e59f77c48268f0b7cfae2bcb34a791213acb1755" }, "video_camera": { "category": "objects", "moji": "📹", + "description": "video camera", "unicodeVersion": "6.0", "digest": "62e56f26c286a7964ef1021f0f23fcb4b38cdcfb5b5af569b472340c412c619a" }, "video_game": { "category": "activity", "moji": "🎮", + "description": "video game", "unicodeVersion": "6.0", "digest": "2787e302aa9e6fd7e9dc382c9bc7f5fbf244ef4940e08a4f9e80d33324f3032e" }, "violin": { "category": "activity", "moji": "🎻", + "description": "violin", "unicodeVersion": "6.0", "digest": "1e69d531ce2b5d5bf1dd9470187dbbe76f479d14428834b6a9e2bf5296dc0ec9" }, "virgo": { "category": "symbols", "moji": "♍", + "description": "virgo", "unicodeVersion": "1.1", "digest": "0f75e9c228bc467fd0cec0f93f0e087c943bc5fb1d945fb0d4de53d07718388e" }, "volcano": { "category": "travel", "moji": "🌋", + "description": "volcano", "unicodeVersion": "6.0", "digest": "41c92ef88ca533df342a0ebe59d2b676873bfa944c3988495b8a96060a9b8e16" }, "volleyball": { "category": "activity", "moji": "🏐", + "description": "volleyball", "unicodeVersion": "8.0", "digest": "774a83357f7aee890b4d4383236f0a90946dbd7c86aaabadc5753dcc9b4c9d69" }, "vs": { "category": "symbols", "moji": "🆚", + "description": "squared vs", "unicodeVersion": "6.0", "digest": "ac943e4c737459c2e1adbac8b71d3fdaebb704dbaf5713012e7a77beb09db1ef" }, "vulcan": { "category": "people", "moji": "🖖", + "description": "raised hand with part between middle and ring fingers", "unicodeVersion": "7.0", "digest": "b4d409a0b019e7b06333cefd15ea46cb54aef5132d86e8ba361c1c3b911fe265" }, "vulcan_tone1": { "category": "people", "moji": "🖖🏻", + "description": "raised hand with part between middle and ring fingers tone 1", "unicodeVersion": "8.0", "digest": "cc6072c85031b5081995f98a57f09ab177168318f69a51f3acc63251760499a4" }, "vulcan_tone2": { "category": "people", "moji": "🖖🏼", + "description": "raised hand with part between middle and ring fingers tone 2", "unicodeVersion": "8.0", "digest": "858bd5a1ac91dc4d7735f57ba4dd69d39138aa6dac1c80cfc05de30a59a5bc33" }, "vulcan_tone3": { "category": "people", "moji": "🖖🏽", + "description": "raised hand with part between middle and ring fingers tone 3", "unicodeVersion": "8.0", "digest": "2f74b6f3eab2a75063591b66f1c7350af0d23153e1427af91de20c48a5f4a54a" }, "vulcan_tone4": { "category": "people", "moji": "🖖🏾", + "description": "raised hand with part between middle and ring fingers tone 4", "unicodeVersion": "8.0", "digest": "87cf8b87d3610f742857a9704b658462df32b4924d8f1ddba26f761e738c4e11" }, "vulcan_tone5": { "category": "people", "moji": "🖖🏿", + "description": "raised hand with part between middle and ring fingers tone 5", "unicodeVersion": "8.0", "digest": "11e9ff62f2385edeb477dbf66c63734536531def5771daf80b66a3425ac71493" }, "walking": { "category": "people", "moji": "🚶", + "description": "pedestrian", "unicodeVersion": "6.0", "digest": "ae77471fe1e8a734d11711cdb589f64347c35d6ee2fc10f6db16ac550c0557fa" }, "walking_tone1": { "category": "people", "moji": "🚶🏻", + "description": "pedestrian tone 1", "unicodeVersion": "8.0", "digest": "3de871c234e1340ccf95338df7babd94d175cfcb17a57b5a74d950e0a31f03b1" }, "walking_tone2": { "category": "people", "moji": "🚶🏼", + "description": "pedestrian tone 2", "unicodeVersion": "8.0", "digest": "620eb7bfb753a331a5822b02bdaf08d8dde7b573efd210287a3d3dfdd84a40b9" }, "walking_tone3": { "category": "people", "moji": "🚶🏽", + "description": "pedestrian tone 3", "unicodeVersion": "8.0", "digest": "ff39545acc2256006128f8c186433c28052b8c9aaec46fe06f25cff02c71f6b8" }, "walking_tone4": { "category": "people", "moji": "🚶🏾", + "description": "pedestrian tone 4", "unicodeVersion": "8.0", "digest": "a9499d142392977a9b9e54fb957952359e9bdffce7ec2f1e8320523d185fb066" }, "walking_tone5": { "category": "people", "moji": "🚶🏿", + "description": "pedestrian tone 5", "unicodeVersion": "8.0", "digest": "b47a4c48ce40298f842f454fc1abccae70f69725d73ee2c80e4018f4c4065d7d" }, "waning_crescent_moon": { "category": "nature", "moji": "🌘", + "description": "waning crescent moon symbol", "unicodeVersion": "6.0", "digest": "2ec7896eefcf821e0ea013556a17af59e997503662c07f080d0a84ab13ef4cf1" }, "waning_gibbous_moon": { "category": "nature", "moji": "🌖", + "description": "waning gibbous moon symbol", "unicodeVersion": "6.0", "digest": "ce2f5aca8fccdacaaf174d10da4e493e853e4608cc4d159aa3081d108a8b58d5" }, "warning": { "category": "symbols", "moji": "⚠", + "description": "warning sign", "unicodeVersion": "4.0", "digest": "745f1d203958f42bf37ecb5909cd0819934e300308ba0ff20964c8c203092f90" }, "wastebasket": { "category": "objects", "moji": "🗑", + "description": "wastebasket", "unicodeVersion": "7.0", "digest": "221a1b6d9975051038d9d97e18a16556cdf4254a6bca4c29bf1c51f306c79f2a" }, "watch": { "category": "objects", "moji": "⌚", + "description": "watch", "unicodeVersion": "1.1", "digest": "acc0c96751404a789b3085f10425cf34f942185215df459515d2439cde3efc6b" }, "water_buffalo": { "category": "nature", "moji": "🐃", + "description": "water buffalo", "unicodeVersion": "6.0", "digest": "ba6a840d4f57f8f9f3e9f29b8a030faf02a3a3d912e3e31b067616b2ac48a3d1" }, "water_polo": { "category": "activity", "moji": "🤽", + "description": "water polo", "unicodeVersion": "9.0", "digest": "fc77e1d2a84a9f4cf0cf19c1ea10cf137cf0940b9103a523121eda87677ad148" }, "water_polo_tone1": { "category": "activity", "moji": "🤽🏻", + "description": "water polo tone 1", "unicodeVersion": "9.0", "digest": "3be28384edd29ada8109f07720d601a9d5866ed63e6234efe9ee1a194ed5d0c5" }, "water_polo_tone2": { "category": "activity", "moji": "🤽🏼", + "description": "water polo tone 2", "unicodeVersion": "9.0", "digest": "afcd3f28c6719f869ca79a6fd1ccade2ea976ade844fbc1081fc72865bcb652f" }, "water_polo_tone3": { "category": "activity", "moji": "🤽🏽", + "description": "water polo tone 3", "unicodeVersion": "9.0", "digest": "d19481c9b82d9413e99c2652e020fd763f2b54408dedaffec8dfe80973ded407" }, "water_polo_tone4": { "category": "activity", "moji": "🤽🏾", + "description": "water polo tone 4", "unicodeVersion": "9.0", "digest": "375972d882b627e8d525e632e58b30346fc3e01858d7d08d62a9d3bf8132bbc7" }, "water_polo_tone5": { "category": "activity", "moji": "🤽🏿", + "description": "water polo tone 5", "unicodeVersion": "9.0", "digest": "a8e1ced1c5382a8147a1d1801a133cada9a0e52e41de6272e56c3c1f426f6048" }, "watermelon": { "category": "food", "moji": "🍉", + "description": "watermelon", "unicodeVersion": "6.0", "digest": "42a3821d2e4dd595c93f5db7a5c70b7af486b8f0ddd3b9d26bc4e743a88e699a" }, "wave": { "category": "people", "moji": "👋", + "description": "waving hand sign", "unicodeVersion": "6.0", "digest": "cddbd764d471604446cbaca91f77f6c4119d1cfc2c856732ca0eaac4593cb736" }, "wave_tone1": { "category": "people", "moji": "👋🏻", + "description": "waving hand sign tone 1", "unicodeVersion": "8.0", "digest": "cf40797437ddf68ec0275f337e6aac4bed81e28da7636d56c9f817ddf8e2b30a" }, "wave_tone2": { "category": "people", "moji": "👋🏼", + "description": "waving hand sign tone 2", "unicodeVersion": "8.0", "digest": "12c8a3e82c03ee35a734c642be482ba2d9d5948dacf91ec1fda243316dd4a0d0" }, "wave_tone3": { "category": "people", "moji": "👋🏽", + "description": "waving hand sign tone 3", "unicodeVersion": "8.0", "digest": "ebcaef43e21b475f76de811d4f4d1a67d9393973b57b03876e02164345a2ba4a" }, "wave_tone4": { "category": "people", "moji": "👋🏾", + "description": "waving hand sign tone 4", "unicodeVersion": "8.0", "digest": "7df7b70cf76766836ba146c3d91b6104930c384450cf2688426e60c1c06a1fc8" }, "wave_tone5": { "category": "people", "moji": "👋🏿", + "description": "waving hand sign tone 5", "unicodeVersion": "8.0", "digest": "8dfdba6aeff5d7dfd807467d431a137547726b34d021f1a5a0b74e155d270ea7" }, "wavy_dash": { "category": "symbols", "moji": "〰", + "description": "wavy dash", "unicodeVersion": "1.1", "digest": "7b1968474f01d12fd09a1f2572282927138d9e9d6a3642de4bf68af80a8c3738" }, "waxing_crescent_moon": { "category": "nature", "moji": "🌒", + "description": "waxing crescent moon symbol", "unicodeVersion": "6.0", "digest": "852d7e55a19074d061fa3aa80d6b1e7e87a9280bdf44d94bbdbbe6d59178b1be" }, "waxing_gibbous_moon": { "category": "nature", "moji": "🌔", + "description": "waxing gibbous moon symbol", "unicodeVersion": "6.0", "digest": "a3a1c7cc72521a3f74929789a90e1c35d81ac86e21225c9f844d718d8940e3b3" }, "wc": { "category": "symbols", "moji": "🚾", + "description": "water closet", "unicodeVersion": "6.0", "digest": "4b95d54e0b53e4b705277917653503b32d6a143c2eaf6c547bc8e01c2dc23659" }, "weary": { "category": "people", "moji": "😩", + "description": "weary face", "unicodeVersion": "6.0", "digest": "3528f85540996cd5b562efe5421c495fc1bb414dc797bc20062783ae1b730847" }, "wedding": { "category": "travel", "moji": "💒", + "description": "wedding", "unicodeVersion": "6.0", "digest": "980f3522cc4c19c3096e668032ea2cd19e7900cdc4b73bbb1c9b4c4d28dc78af" }, "whale": { "category": "nature", "moji": "🐳", + "description": "spouting whale", "unicodeVersion": "6.0", "digest": "6368fe4bc4a7f68aa2bd5386686a5f1b159feacbec16d59515f2b6e5d01adfbd" }, "whale2": { "category": "nature", "moji": "🐋", + "description": "whale", "unicodeVersion": "6.0", "digest": "ccd3edf88167965f2abc18631ffb80e2532f728da35bc0c11144376685da18e8" }, "wheel_of_dharma": { "category": "symbols", "moji": "☸", + "description": "wheel of dharma", "unicodeVersion": "1.1", "digest": "4a0a13fcd507b9621686c8090bf340aa8770c064e0e3eb576fbae1229000d6da" }, "wheelchair": { "category": "symbols", "moji": "♿", + "description": "wheelchair symbol", "unicodeVersion": "4.1", "digest": "f5250f2b4b5b4ffe6a6f77d30865c3f5d7173fc91aee547869589b2a96da91c8" }, "white_check_mark": { "category": "symbols", "moji": "✅", + "description": "white heavy check mark", "unicodeVersion": "6.0", "digest": "45eb17bde6e503f22c8579d6e4d507ad6557a15f9eaad14aa716ec9ba1540876" }, "white_circle": { "category": "symbols", "moji": "⚪", + "description": "medium white circle", "unicodeVersion": "4.1", "digest": "2e7323fa4d1e3929e529d49210a0b82a043eae4f7c95128ec86b98c46fdb0e7c" }, "white_flower": { "category": "symbols", "moji": "💮", + "description": "white flower", "unicodeVersion": "6.0", "digest": "ace093b310eeefdecf4a4bdaf4fbcbb568457b0191ac80778a466ac5f3f4025a" }, "white_large_square": { "category": "symbols", "moji": "⬜", + "description": "white large square", "unicodeVersion": "5.1", "digest": "0db6957ee9ff7325b534b730fc05345a63d4ed9060f0f816807d0dcf004baa3e" }, "white_medium_small_square": { "category": "symbols", "moji": "◽", + "description": "white medium small square", "unicodeVersion": "3.2", "digest": "d79689981a7b38211c60a025a81e44fd39ac6ea4062e227cae3aab8f51572cd4" }, "white_medium_square": { "category": "symbols", "moji": "◻", + "description": "white medium square", "unicodeVersion": "3.2", "digest": "6c4ce26d3f69667219f29ea18b04f3e79373024426275f25936e09a683e9a4fc" }, "white_small_square": { "category": "symbols", "moji": "▫", + "description": "white small square", "unicodeVersion": "1.1", "digest": "ae0d35a6bbba4592b89b2f0f1f2d183efb2f93cf2a2136c0c195aab72f0bb1c8" }, "white_square_button": { "category": "symbols", "moji": "🔳", + "description": "white square button", "unicodeVersion": "6.0", "digest": "797f3d9e44e88e940ffc118e52d0f709eec2ef14b13bdf873ad4b0c96cc0b042" }, "white_sun_cloud": { "category": "nature", "moji": "🌥", + "description": "white sun behind cloud", "unicodeVersion": "7.0", "digest": "0e714038bb0a5b091dd4ad8829c5c72dece493e09da6d56ceadcd0b68e1c0fd5" }, "white_sun_rain_cloud": { "category": "nature", "moji": "🌦", + "description": "white sun behind cloud with rain", "unicodeVersion": "7.0", "digest": "82fb2a91d43c7c511afed216e12f98e32aef4475e7f3c7ccc0f39732d2f7d5e5" }, "white_sun_small_cloud": { "category": "nature", "moji": "🌤", + "description": "white sun with small cloud", "unicodeVersion": "7.0", "digest": "0a6164cdadf2413555b7ef47b95f823f5a010f36d2dacfb1a38335a0f59e9601" }, "wilted_rose": { "category": "nature", "moji": "🥀", + "description": "wilted flower", "unicodeVersion": "9.0", "digest": "2c9e01ab9a61d057c71478b09ba7d82ae08f4a5a1c2212b7ad562b74f616677f" }, "wind_blowing_face": { "category": "nature", "moji": "🌬", + "description": "wind blowing face", "unicodeVersion": "7.0", "digest": "e4f63149cbc8829118571f6a93487b96d26665fc15d17d578cca4e5c752cd54f" }, "wind_chime": { "category": "objects", "moji": "🎐", + "description": "wind chime", "unicodeVersion": "6.0", "digest": "1b1b212fbd74a9edc62aee7ffab9bcf91d3a9f69bffb2be4b7fd527914c14ced" }, "wine_glass": { "category": "food", "moji": "🍷", + "description": "wine glass", "unicodeVersion": "6.0", "digest": "d99107d6809386bc5e219aa58ee4930d27b7c3a6d2b10deb9f523df369f766d1" }, "wink": { "category": "people", "moji": "😉", + "description": "winking face", "unicodeVersion": "6.0", "digest": "56e29994a47335a901d0c98fa141d26faae8f647a860517bd3615fa980921885" }, "wolf": { "category": "nature", "moji": "🐺", + "description": "wolf face", "unicodeVersion": "6.0", "digest": "4a983f5ec8ec0872fcde7890e17605b1229064e5e194b6fca1c4259068d1caed" }, "woman": { "category": "people", "moji": "👩", + "description": "woman", "unicodeVersion": "6.0", "digest": "a06a22a48eeb3aeb885321358fe234e97797ed33be17f52d232ce2830cfbcd97" }, "woman_tone1": { "category": "people", "moji": "👩🏻", + "description": "woman tone 1", "unicodeVersion": "8.0", "digest": "c2e4b135c1dac6a0b002569a6ccd9d098f6cb18481c68b5d9115e11241a0978d" }, "woman_tone2": { "category": "people", "moji": "👩🏼", + "description": "woman tone 2", "unicodeVersion": "8.0", "digest": "4848e650051214a53c4cd9f6d3d94158f77f65ecb34f891789de34ee0a713006" }, "woman_tone3": { "category": "people", "moji": "👩🏽", + "description": "woman tone 3", "unicodeVersion": "8.0", "digest": "b6f751ad47da019cdfb9d6d78f9610adb92120abf204c30df79a9150b57dbdee" }, "woman_tone4": { "category": "people", "moji": "👩🏾", + "description": "woman tone 4", "unicodeVersion": "8.0", "digest": "fd27d3a669dc34313fbfe518df7dc2ded3ade5dde695f8d773afe87bf8a8b0d4" }, "woman_tone5": { "category": "people", "moji": "👩🏿", + "description": "woman tone 5", "unicodeVersion": "8.0", "digest": "9ae9b14dfff40fa60a565d89479727feeba4fd6ffea9acb353a81b14aba751d4" }, "womans_clothes": { "category": "people", "moji": "👚", + "description": "womans clothes", "unicodeVersion": "6.0", "digest": "d12a27810780fe5cd8118ed4587e0c4e70dbe9bcd014c6866fe6a8c9c7c55698" }, "womans_hat": { "category": "people", "moji": "👒", + "description": "womans hat", "unicodeVersion": "6.0", "digest": "52a0255b3483085bd125d39b74516ab6a81003964f44995c2fac821e7ff93086" }, "womens": { "category": "symbols", "moji": "🚺", + "description": "womens symbol", "unicodeVersion": "6.0", "digest": "7e38964006f8b28dfa2b3e9b2b16553bb50c18a63455f556b0bff35ee172137e" }, "worried": { "category": "people", "moji": "😟", + "description": "worried face", "unicodeVersion": "6.1", "digest": "5a073985e1344bc34201ef94a491f7f2b946f5828c9fdbc57eeb2dcd87ac3a6b" }, "wrench": { "category": "objects", "moji": "🔧", + "description": "wrench", "unicodeVersion": "6.0", "digest": "81aae53bc892035b905bf3ec5b442a8ecc95027c5fa9eb51b7c3e7d8fad3f3f4" }, "wrestlers": { "category": "activity", "moji": "🤼", + "description": "wrestlers", "unicodeVersion": "9.0", "digest": "9be983f3f9438f3ab8f6b643a958371d1e710c6d78e728f3465141811f05c2d5" }, "wrestlers_tone1": { "category": "activity", "moji": "🤼🏻", + "description": "wrestlers tone 1", "unicodeVersion": "9.0", "digest": "60461f83bfc93ce59dd027eab4782b7f206a7b142719fa72f301e047dc83a5d9" }, "wrestlers_tone2": { "category": "activity", "moji": "🤼🏼", + "description": "wrestlers tone 2", "unicodeVersion": "9.0", "digest": "67ad93c86e6c58d552c18e7a0105cc81fd9bb0474da51f788eba2e4c14b4a636" }, "wrestlers_tone3": { "category": "activity", "moji": "🤼🏽", + "description": "wrestlers tone 3", "unicodeVersion": "9.0", "digest": "6bfd06c4435cabf2def153912040e05bf8db424fa383148ddda6d0ce8a8a3349" }, "wrestlers_tone4": { "category": "activity", "moji": "🤼🏾", + "description": "wrestlers tone 4", "unicodeVersion": "9.0", "digest": "597312678834c4d288c238482879856d5eba4620deb1eaef495f428e2ba5f2a5" }, "wrestlers_tone5": { "category": "activity", "moji": "🤼🏿", + "description": "wrestlers tone 5", "unicodeVersion": "9.0", "digest": "d6aebdf1e44fd825b9a5b3716aefbc53f4b4dbb73cb2a628c0f2994ebfd34614" }, "writing_hand": { "category": "people", "moji": "✍", + "description": "writing hand", "unicodeVersion": "1.1", "digest": "110517ae4da5587e8b0662881658e27da4120bfacec54734fd6657831d4d782f" }, "writing_hand_tone1": { "category": "people", "moji": "✍🏻", + "description": "writing hand tone 1", "unicodeVersion": "8.0", "digest": "2c7e2108e1990490b681343c1b01b4183d4f18fbdef792f113b2f87595e0dad0" }, "writing_hand_tone2": { "category": "people", "moji": "✍🏼", + "description": "writing hand tone 2", "unicodeVersion": "8.0", "digest": "87ec8d44f472d301adbcbd50d8c852b609e46584057f59cc1527401db363c1bf" }, "writing_hand_tone3": { "category": "people", "moji": "✍🏽", + "description": "writing hand tone 3", "unicodeVersion": "8.0", "digest": "4a48ddef91f7264e8fa9cca223554db22b3a2e3153e94b88d146644ea6dd661e" }, "writing_hand_tone4": { "category": "people", "moji": "✍🏾", + "description": "writing hand tone 4", "unicodeVersion": "8.0", "digest": "e5254564a1f91e42ee59f359d8cd26f52abdc04dca8f3b37cb2f140cb7f71390" }, "writing_hand_tone5": { "category": "people", "moji": "✍🏿", + "description": "writing hand tone 5", "unicodeVersion": "8.0", "digest": "61299bf86d83d323ca3e6052c535ae66c6f7b3d9866a37db0464223b8bc28523" }, "x": { "category": "symbols", "moji": "❌", + "description": "cross mark", "unicodeVersion": "6.0", "digest": "3e5a7918e31ddefdf1ce73972365e2f0bfd2917d6a450c1a278c108349c9425d" }, "yellow_heart": { "category": "symbols", "moji": "💛", + "description": "yellow heart", "unicodeVersion": "6.0", "digest": "a1098f2f04c29754cc9974324508386787d4d803b57cf691d42de414cb2679d6" }, "yen": { "category": "objects", "moji": "💴", + "description": "banknote with yen sign", "unicodeVersion": "6.0", "digest": "944daaeb3f6369c807c0e63b106cee1360040f7800a70c0d942a992f25a55da7" }, "yin_yang": { "category": "symbols", "moji": "☯", + "description": "yin yang", "unicodeVersion": "1.1", "digest": "5ee8d13dacf41306a09237bfcff6abeef110331b40eb7d6e80600628c1327545" }, "yum": { "category": "people", "moji": "😋", + "description": "face savouring delicious food", "unicodeVersion": "6.0", "digest": "31a89088c21bd7a74a3a26d731a907d1bc49436300a9f9c55248703cf7ef44c7" }, "zap": { "category": "nature", "moji": "⚡", + "description": "high voltage sign", "unicodeVersion": "4.0", "digest": "9f8144ae6f866129aea41bbf694b0c858ef9352a139969e57cd8db73385f52c3" }, "zero": { "category": "symbols", "moji": "0️⃣", + "description": "keycap digit zero", "unicodeVersion": "3.0", "digest": "1b27b5c904defadbdd28ace67a6be5c277ff043297db7cd9f672bbf84e37fa1a" }, "zipper_mouth": { "category": "people", "moji": "🤐", + "description": "zipper-mouth face", "unicodeVersion": "8.0", "digest": "81bee5aa1202dfd5a4c7badb71ec0e44b8f75c2cbef94e6fd35c593d8770ae43" }, "zzz": { "category": "people", "moji": "💤", + "description": "sleeping symbol", "unicodeVersion": "6.0", "digest": "b3313d0c44a59fa9d4ce9f7eb4d07ff71dfc8bb01798154250f27cdcf3c693b5" } diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake index 5293f5af12d..f6155b2ff2e 100644 --- a/lib/tasks/gemojione.rake +++ b/lib/tasks/gemojione.rake @@ -1,6 +1,6 @@ namespace :gemojione do desc 'Generates Emoji SHA256 digests' - task digests: ['yarn:check', 'environment'] do + task digests: ['environment'] do require 'digest/sha2' require 'json' @@ -19,6 +19,7 @@ namespace :gemojione do entry = { category: emoji_hash['category'], moji: emoji_hash['moji'], + description: emoji_hash['description'], unicodeVersion: Gitlab::Emoji.emoji_unicode_version(name), digest: hash_digest, } -- cgit v1.2.1 From 4bde6eae51a43f37326f53363e6e4f21c5a3c295 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 24 Apr 2017 14:28:47 +0100 Subject: Added back yarn:check to rake task... --- lib/tasks/gemojione.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake index f6155b2ff2e..b5572a39d30 100644 --- a/lib/tasks/gemojione.rake +++ b/lib/tasks/gemojione.rake @@ -1,6 +1,6 @@ namespace :gemojione do desc 'Generates Emoji SHA256 digests' - task digests: ['environment'] do + task digests: ['yarn:check', 'environment'] do require 'digest/sha2' require 'json' -- cgit v1.2.1 From 3f2578bce5afbb1f92acab2481ec6de2f38a6296 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 21 Apr 2017 15:09:37 +0100 Subject: Commit view correctly spans the full width when parallel view Closes #30881 --- app/assets/stylesheets/pages/issuable.scss | 11 ++++++++--- app/views/projects/commit/show.html.haml | 4 +++- .../unreleased/commit-limited-container-width.yml | 4 ++++ spec/features/projects/commit/container_spec.rb | 23 ++++++++++++++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/commit-limited-container-width.yml create mode 100644 spec/features/projects/commit/container_spec.rb diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 8d3d1a72b9b..cf8735e6fad 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -6,7 +6,13 @@ } .limit-container-width { - .detail-page-header { + .detail-page-header, + .page-content-header, + .commit-box, + .info-well, + .notes, + .commit-ci-menu, + .files-changed { @extend .fixed-width-container; } @@ -36,8 +42,7 @@ } .diffs { - .mr-version-controls, - .files-changed { + .mr-version-controls { @extend .fixed-width-container; } } diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 0d11da2451a..cdf0f11dc5f 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,9 +1,11 @@ - @no_container = true +- container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : '' +- limited_container_width = fluid_layout || diff_view == :inline ? '' : 'limit-container-width' - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_description @commit.description = render "projects/commits/head" -%div{ class: container_class } +%div.container-fluid{ class: [limited_container_width, container_class] } = render "commit_box" - if @commit.status = render "ci_menu" diff --git a/changelogs/unreleased/commit-limited-container-width.yml b/changelogs/unreleased/commit-limited-container-width.yml new file mode 100644 index 00000000000..253646b13da --- /dev/null +++ b/changelogs/unreleased/commit-limited-container-width.yml @@ -0,0 +1,4 @@ +--- +title: Side-by-side view in commits correcly expands full window width +merge_request: +author: diff --git a/spec/features/projects/commit/container_spec.rb b/spec/features/projects/commit/container_spec.rb new file mode 100644 index 00000000000..80b758ec14f --- /dev/null +++ b/spec/features/projects/commit/container_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe 'Commit container', :js, :feature do + let(:user) { create(:user) } + let(:project) { create(:project) } + + before do + project.team << [user, :master] + login_as(user) + end + + it 'keeps container-limited when view type is inline' do + visit namespace_project_commit_path(project.namespace, project, project.commit.id, view: :inline) + + expect(page).not_to have_selector('.limit-container-width') + end + + it 'diff spans full width when view type is parallel' do + visit namespace_project_commit_path(project.namespace, project, project.commit.id, view: :parallel) + + expect(page).to have_selector('.limit-container-width') + end +end -- cgit v1.2.1 From 064739431a837ea644851f3f3d01f5da6ee05951 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 24 Apr 2017 16:20:23 +0100 Subject: Fixed HAML lint --- app/views/projects/commit/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index cdf0f11dc5f..16d2646cb4e 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -5,7 +5,7 @@ - page_description @commit.description = render "projects/commits/head" -%div.container-fluid{ class: [limited_container_width, container_class] } +.container-fluid{ class: [limited_container_width, container_class] } = render "commit_box" - if @commit.status = render "ci_menu" -- cgit v1.2.1 From 81a37ca1ee6d57e24633921fb6edee319f18887b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 24 Apr 2017 17:05:59 +0100 Subject: Moved the title onto the emoji tag This adds the description in all places emojis are used --- app/assets/javascripts/awards_handler.js | 17 +++++------------ app/assets/javascripts/behaviors/gl_emoji.js | 1 + lib/gitlab/emoji.rb | 2 +- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index e0c592ef73d..adb45b0606d 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -29,18 +29,11 @@ const categoryLabelMap = { flags: 'Flags', }; -function createEmojiObject(alias) { - return { - alias, - description: emojiMap[alias].description, - }; -}; - function buildCategoryMap() { return Object.keys(emojiMap).reduce((currentCategoryMap, emojiNameKey) => { const emojiInfo = emojiMap[emojiNameKey]; if (currentCategoryMap[emojiInfo.category]) { - currentCategoryMap[emojiInfo.category].push(createEmojiObject(emojiNameKey)); + currentCategoryMap[emojiInfo.category].push(emojiNameKey); } return currentCategoryMap; @@ -62,10 +55,10 @@ function renderCategory(name, emojiList, opts = {}) { ${name}
      - ${emojiList.map(emoji => ` + ${emojiList.map(emojiName => `
    • - @@ -505,7 +498,7 @@ AwardsHandler.prototype.getFrequentlyUsedEmojis = function getFrequentlyUsedEmoj const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(',')); this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter( inputName => isEmojiNameValid(inputName), - ).map(emojiNameKey => createEmojiObject(emojiNameKey)); + ); return this.frequentlyUsedEmojis; })(); diff --git a/app/assets/javascripts/behaviors/gl_emoji.js b/app/assets/javascripts/behaviors/gl_emoji.js index 19a607309e4..f3863d9e495 100644 --- a/app/assets/javascripts/behaviors/gl_emoji.js +++ b/app/assets/javascripts/behaviors/gl_emoji.js @@ -62,6 +62,7 @@ function glEmojiTag(inputName, options) { data-fallback-src="${fallbackImageSrc}" ${fallbackSpriteAttribute} data-unicode-version="${emojiInfo.unicodeVersion}" + title=${emojiInfo.description} > ${contents} diff --git a/lib/gitlab/emoji.rb b/lib/gitlab/emoji.rb index a16d9fc2265..e3e36b35ce9 100644 --- a/lib/gitlab/emoji.rb +++ b/lib/gitlab/emoji.rb @@ -54,7 +54,7 @@ module Gitlab unicode_version: emoji_unicode_version(emoji_name) } - ActionController::Base.helpers.content_tag('gl-emoji', emoji_info['moji'], data: data) + ActionController::Base.helpers.content_tag('gl-emoji', emoji_info['moji'], title: emoji_info['description'], data: data) end end end -- cgit v1.2.1 From 5bf0441fd9cda2dcc3c3ef1ce3084df5ed87602d Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 24 Apr 2017 19:55:07 +0200 Subject: Cleanup deploymentsd controller spec and use schema validation to test the output --- .../projects/deployments_controller_spec.rb | 13 ++--- spec/fixtures/api/schemas/deployments.json | 58 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 spec/fixtures/api/schemas/deployments.json diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb index fbe510fb539..89692b601b2 100644 --- a/spec/controllers/projects/deployments_controller_spec.rb +++ b/spec/controllers/projects/deployments_controller_spec.rb @@ -6,16 +6,15 @@ describe Projects::DeploymentsController do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:environment) { create(:environment, name: 'production', project: project) } - let(:deployment) { create(:deployment, project: project, environment: environment) } before do - project.team << [user, :master] + project.add_master(user) sign_in(user) end describe 'GET #index' do - it 'returns list of deployments withing last 8 hours' do + it 'returns list of deployments from last 8 hours' do create(:deployment, environment: environment, created_at: 9.hours.ago) create(:deployment, environment: environment, created_at: 7.hours.ago) create(:deployment, environment: environment) @@ -31,13 +30,9 @@ describe Projects::DeploymentsController do create(:deployment, environment: environment) get :index, environment_params - expect(response).to be_ok - - deployments = json_response['deployments'] - deployment_info = deployments.first.with_indifferent_access - expect(deployments.count).to eq(1) - expect(deployment_info).to include(:id, :iid, :sha, :ref, :tag, :created_at) + expect(response).to be_ok + expect(response).to match_response_schema('deployments') end end diff --git a/spec/fixtures/api/schemas/deployments.json b/spec/fixtures/api/schemas/deployments.json new file mode 100644 index 00000000000..1112f23aab2 --- /dev/null +++ b/spec/fixtures/api/schemas/deployments.json @@ -0,0 +1,58 @@ +{ + "additionalProperties": false, + "properties": { + "deployments": { + "items": { + "additionalProperties": false, + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "iid": { + "type": "integer" + }, + "last?": { + "type": "boolean" + }, + "ref": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "sha": { + "type": "string" + }, + "tag": { + "type": "boolean" + } + }, + "required": [ + "sha", + "created_at", + "iid", + "tag", + "last?", + "ref", + "id" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "deployments" + ], + "type": "object" +} -- cgit v1.2.1 From 6dab3033c9c2f6a9a4cabf9daffeff2b914cf66c Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 24 Apr 2017 19:55:47 +0200 Subject: Add deployments security check --- spec/features/security/project/private_access_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index a8fc0624588..c9059ef8fa6 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -417,6 +417,21 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } end + describe "GET /:project_path/environments/:id/deployments" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for(:admin) } + it { is_expected.to be_allowed_for(:owner).of(project) } + it { is_expected.to be_allowed_for(:master).of(project) } + it { is_expected.to be_allowed_for(:developer).of(project) } + it { is_expected.to be_allowed_for(:reporter).of(project) } + it { is_expected.to be_denied_for(:guest).of(project) } + it { is_expected.to be_denied_for(:user) } + it { is_expected.to be_denied_for(:external) } + it { is_expected.to be_denied_for(:visitor) } + end + describe "GET /:project_path/environments/new" do subject { new_namespace_project_environment_path(project.namespace, project) } -- cgit v1.2.1 From e1e0b763dcc98794c6f85ac2adeb4ac0ab9a7e7b Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 24 Apr 2017 20:00:00 +0200 Subject: Fix deployments security check path and add checks for internal and public access --- spec/features/security/project/internal_access_spec.rb | 15 +++++++++++++++ spec/features/security/project/private_access_spec.rb | 2 +- spec/features/security/project/public_access_spec.rb | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 6ecdc8cbb71..00d5645c7ac 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -428,6 +428,21 @@ describe "Internal Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } end + describe "GET /:project_path/environments/:id/deployments" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_deployments_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for(:admin) } + it { is_expected.to be_allowed_for(:owner).of(project) } + it { is_expected.to be_allowed_for(:master).of(project) } + it { is_expected.to be_allowed_for(:developer).of(project) } + it { is_expected.to be_allowed_for(:reporter).of(project) } + it { is_expected.to be_denied_for(:guest).of(project) } + it { is_expected.to be_denied_for(:user) } + it { is_expected.to be_denied_for(:external) } + it { is_expected.to be_denied_for(:visitor) } + end + describe "GET /:project_path/environments/new" do subject { new_namespace_project_environment_path(project.namespace, project) } diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index c9059ef8fa6..52b858e780d 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -419,7 +419,7 @@ describe "Private Project Access", feature: true do describe "GET /:project_path/environments/:id/deployments" do let(:environment) { create(:environment, project: project) } - subject { namespace_project_environment_path(project.namespace, project, environment) } + subject { namespace_project_environment_deployments_path(project.namespace, project, environment) } it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:owner).of(project) } diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index c4d2f50ca14..761cedf8157 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -248,6 +248,21 @@ describe "Public Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } end + describe "GET /:project_path/environments/:id/deployments" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_deployments_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for(:admin) } + it { is_expected.to be_allowed_for(:owner).of(project) } + it { is_expected.to be_allowed_for(:master).of(project) } + it { is_expected.to be_allowed_for(:developer).of(project) } + it { is_expected.to be_allowed_for(:reporter).of(project) } + it { is_expected.to be_denied_for(:guest).of(project) } + it { is_expected.to be_denied_for(:user) } + it { is_expected.to be_denied_for(:external) } + it { is_expected.to be_denied_for(:visitor) } + end + describe "GET /:project_path/environments/new" do subject { new_namespace_project_environment_path(project.namespace, project) } -- cgit v1.2.1 From e057ad975c6ced8c9565fc8329db16b1ababcef8 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 24 Apr 2017 21:34:21 +0200 Subject: DeploymentsSerializer can serialize arrays properly --- app/controllers/projects/deployments_controller.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb index a8a6f09d9df..79e59fc1326 100644 --- a/app/controllers/projects/deployments_controller.rb +++ b/app/controllers/projects/deployments_controller.rb @@ -1,14 +1,13 @@ class Projects::DeploymentsController < Projects::ApplicationController + before_action :authorize_read_environment! before_action :authorize_read_deployment! def index - serializer = DeploymentSerializer.new(user: @current_user, project: project) - deployments = environment.deployments.reorder(created_at: :desc) deployments = deployments.where('created_at > ?', params[:after].to_time) if params[:after]&.to_time - deployments = deployments.map { |deployment| serializer.represent(deployment) } - render json: { deployments: deployments } + render json: { deployments: DeploymentSerializer.new(user: @current_user, project: project) + .represent(deployments) } end private -- cgit v1.2.1 From caadfe4cfdb2df56ea4b8bd104c94e8236fc9f9c Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 24 Apr 2017 14:59:36 -0500 Subject: Use bold class --- app/views/projects/builds/_sidebar.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index 694f524faed..26c892d0fd2 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -48,8 +48,7 @@ - if @build.merge_request %p.build-detail-row %span.build-light-text Merge Request: - = link_to merge_request_path(@build.merge_request) do - %strong= "#{@build.merge_request.to_reference}" + = link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request), class: 'bold' - if @build.duration %p.build-detail-row %span.build-light-text Duration: -- cgit v1.2.1 From 33f7c40f3f7d2b342dcaa839b7cd56599a573c65 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 24 Apr 2017 22:34:36 +0200 Subject: use represent_concise instead of separate entity --- app/controllers/projects/deployments_controller.rb | 2 +- app/serializers/deployment_entity.rb | 12 ++++++++++ app/serializers/deployment_entity_detailed.rb | 15 ------------- app/serializers/deployment_serializer.rb | 5 +++++ .../serializers/deployment_entity_detailed_spec.rb | 26 ---------------------- 5 files changed, 18 insertions(+), 42 deletions(-) delete mode 100644 app/serializers/deployment_entity_detailed.rb delete mode 100644 spec/serializers/deployment_entity_detailed_spec.rb diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb index 79e59fc1326..c319671456d 100644 --- a/app/controllers/projects/deployments_controller.rb +++ b/app/controllers/projects/deployments_controller.rb @@ -7,7 +7,7 @@ class Projects::DeploymentsController < Projects::ApplicationController deployments = deployments.where('created_at > ?', params[:after].to_time) if params[:after]&.to_time render json: { deployments: DeploymentSerializer.new(user: @current_user, project: project) - .represent(deployments) } + .represent_concise(deployments) } end private diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index a8d6d80faad..8b3de1bed0f 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -9,9 +9,21 @@ class DeploymentEntity < Grape::Entity expose :name do |deployment| deployment.ref end + + expose :ref_path do |deployment| + namespace_project_tree_path( + deployment.project.namespace, + deployment.project, + id: deployment.ref) + end end expose :created_at expose :tag expose :last? + + expose :user, using: UserEntity + expose :commit, using: CommitEntity + expose :deployable, using: BuildEntity + expose :manual_actions, using: BuildEntity end diff --git a/app/serializers/deployment_entity_detailed.rb b/app/serializers/deployment_entity_detailed.rb deleted file mode 100644 index 63df61dd51b..00000000000 --- a/app/serializers/deployment_entity_detailed.rb +++ /dev/null @@ -1,15 +0,0 @@ -class DeploymentEntityDetailed < DeploymentEntity - expose :ref do - expose :ref_path do |deployment| - namespace_project_tree_path( - deployment.project.namespace, - deployment.project, - id: deployment.ref) - end - end - - expose :user, using: UserEntity - expose :commit, using: CommitEntity - expose :deployable, using: BuildEntity - expose :manual_actions, using: BuildEntity -end diff --git a/app/serializers/deployment_serializer.rb b/app/serializers/deployment_serializer.rb index 4b4bf6cd526..cba5c3f311f 100644 --- a/app/serializers/deployment_serializer.rb +++ b/app/serializers/deployment_serializer.rb @@ -1,3 +1,8 @@ class DeploymentSerializer < BaseSerializer entity DeploymentEntity + + def represent_concise(resource, opts = {}) + opts[:only] = [:iid, :id, :sha, :created_at, :tag, :last?, :id, ref: [:name]] + represent(resource, opts) + end end diff --git a/spec/serializers/deployment_entity_detailed_spec.rb b/spec/serializers/deployment_entity_detailed_spec.rb deleted file mode 100644 index d28e3fa0665..00000000000 --- a/spec/serializers/deployment_entity_detailed_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'spec_helper' - -describe DeploymentEntityDetailed do - let(:user) { create(:user) } - let(:request) { double('request') } - let(:deployment) { create(:deployment) } - let(:entity) { described_class.new(deployment, request: request) } - subject { entity.as_json } - - before do - allow(request).to receive(:user).and_return(user) - end - - it 'exposes internal deployment id' do - expect(subject).to include(:iid) - end - - it 'exposes nested information about branch' do - expect(subject[:ref][:name]).to eq 'master' - expect(subject[:ref][:ref_path]).not_to be_empty - end - - it 'exposes creation date' do - expect(subject).to include(:created_at) - end -end -- cgit v1.2.1 From 230608e92b3177ae13877f56eb6da051d938d7ed Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 25 Apr 2017 00:02:58 +0200 Subject: Remove concurrent index --- db/migrate/20170327091750_add_created_at_index_to_deployments.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20170327091750_add_created_at_index_to_deployments.rb b/db/migrate/20170327091750_add_created_at_index_to_deployments.rb index 9cc35d08e1a..fd6ed499b80 100644 --- a/db/migrate/20170327091750_add_created_at_index_to_deployments.rb +++ b/db/migrate/20170327091750_add_created_at_index_to_deployments.rb @@ -10,6 +10,6 @@ class AddCreatedAtIndexToDeployments < ActiveRecord::Migration end def down - remove_index :deployments, :created_at + remove_concurrent_index :deployments, :created_at end end -- cgit v1.2.1 From 967019f7792175c9fc010069657a7e09cf278575 Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Thu, 19 Jan 2017 18:11:12 -0600 Subject: Added a description for non project masters or owners for the members settings page Truncated the project name if it exceeds 18 characters --- app/views/projects/project_members/_index.html.haml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/views/projects/project_members/_index.html.haml b/app/views/projects/project_members/_index.html.haml index ab0771b5751..f83521052ed 100644 --- a/app/views/projects/project_members/_index.html.haml +++ b/app/views/projects/project_members/_index.html.haml @@ -6,6 +6,12 @@ %p Add a new member to %strong= @project.name + - else + %p + Members can be added by project + %i Masters + or + %i Owners .col-lg-9 .light.prepend-top-default - if can?(current_user, :admin_project_member, @project) -- cgit v1.2.1 From 49e9fd05bfbb4dfe15b1698cc00883afdb3ef313 Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Mon, 23 Jan 2017 17:35:31 -0600 Subject: Changed the way to truncate the panel to title from ruby to a scss mixin --- app/assets/stylesheets/framework/common.scss | 4 ++++ app/views/projects/project_members/_team.html.haml | 6 ++++-- changelogs/unreleased/26883-members-page-layout-looks-broken.yml | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/26883-members-page-layout-looks-broken.yml diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 0fd7203e72b..f4f285f0fdc 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -425,6 +425,10 @@ table { } .str-truncated { + &-30 { + @include str-truncated(30%); + } + &-60 { @include str-truncated(60%); } diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index 81d57c77edf..b875fa2eec4 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -1,7 +1,9 @@ .panel.panel-default .panel-heading - Members with access to - %strong= @project.name + %span.str-truncated-30 + Members of + %strong + #{@project.name} %span.badge= @project_members.total_count = form_tag namespace_project_settings_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do .form-group diff --git a/changelogs/unreleased/26883-members-page-layout-looks-broken.yml b/changelogs/unreleased/26883-members-page-layout-looks-broken.yml new file mode 100644 index 00000000000..e0e3a529c3e --- /dev/null +++ b/changelogs/unreleased/26883-members-page-layout-looks-broken.yml @@ -0,0 +1,4 @@ +--- +title: Improved UX on project members settings view +merge_request: +author: -- cgit v1.2.1 From 9d6c769f18dace5699454ff211e8bb679420ddab Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Thu, 26 Jan 2017 14:16:56 -0600 Subject: Added a media query when there's a more width available to show more of the title --- app/assets/stylesheets/framework/common.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index f4f285f0fdc..1edea8b997f 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -427,6 +427,9 @@ table { .str-truncated { &-30 { @include str-truncated(30%); + @media (max-width: $screen-xs-max){ + max-width: 90%; + } } &-60 { -- cgit v1.2.1 From 88c772459e093d78823538fbb18f4942046ae2f1 Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Mon, 24 Apr 2017 13:24:51 -0500 Subject: Added flex wrapping --- app/assets/stylesheets/framework/common.scss | 7 --- app/assets/stylesheets/pages/members.scss | 52 ++++++++++++++++++++++ app/views/projects/project_members/_team.html.haml | 6 +-- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 1edea8b997f..0fd7203e72b 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -425,13 +425,6 @@ table { } .str-truncated { - &-30 { - @include str-truncated(30%); - @media (max-width: $screen-xs-max){ - max-width: 90%; - } - } - &-60 { @include str-truncated(60%); } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index be7193bae04..8dbac76e30a 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -133,3 +133,55 @@ right: 160px; } } + +.flex-project-members-panel { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + + @media (max-width: $screen-sm-min) { + display: block; + + .flex-project-title { + vertical-align: top; + display: inline-block; + max-width: 90%; + } + } + + .flex-project-title { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .badge { + height: 17px; + line-height: 16px; + margin-right: 5px; + padding-top: 1px; + padding-bottom: 1px; + } + + .flex-project-members-form { + flex-wrap: nowrap; + white-space: nowrap; + margin-left: auto; + } +} + +.panel { + .panel-heading { + .badge { + margin-top: 0; + } + + @media (max-width: $screen-sm-min) { + .badge { + margin-right: 0; + margin-left: 0; + } + } + } +} \ No newline at end of file diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index b875fa2eec4..7b1a26043e1 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -1,11 +1,11 @@ .panel.panel-default - .panel-heading - %span.str-truncated-30 + .panel-heading.flex-project-members-panel + %span.flex-project-title Members of %strong #{@project.name} %span.badge= @project_members.total_count - = form_tag namespace_project_settings_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do + = form_tag namespace_project_settings_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } -- cgit v1.2.1 From ee0713babb5dc31aa8f7edaa5ba4d8782dae74ca Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Apr 2017 11:23:04 +0100 Subject: Generate fixutre for metrics page --- app/serializers/environment_entity.rb | 2 +- spec/javascripts/fixtures/environments.rb | 30 +++++++++++ .../fixtures/environments/metrics.html.haml | 62 ---------------------- spec/javascripts/monitoring/deployments_spec.js | 2 +- .../monitoring/prometheus_graph_spec.js | 2 +- 5 files changed, 33 insertions(+), 65 deletions(-) create mode 100644 spec/javascripts/fixtures/environments.rb delete mode 100644 spec/javascripts/fixtures/environments/metrics.html.haml diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb index 2152a1dd499..4ff15a78115 100644 --- a/app/serializers/environment_entity.rb +++ b/app/serializers/environment_entity.rb @@ -6,7 +6,7 @@ class EnvironmentEntity < Grape::Entity expose :state expose :external_url expose :environment_type - expose :last_deployment, using: DeploymentEntityDetailed + expose :last_deployment, using: DeploymentEntity expose :stop_action? expose :metrics_path, if: -> (environment, _) { environment.has_metrics? } do |environment| diff --git a/spec/javascripts/fixtures/environments.rb b/spec/javascripts/fixtures/environments.rb new file mode 100644 index 00000000000..3474f4696ef --- /dev/null +++ b/spec/javascripts/fixtures/environments.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe Projects::EnvironmentsController, '(JavaScript fixtures)', type: :controller do + include JavaScriptFixturesHelpers + + let(:admin) { create(:admin) } + let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:project) { create(:project_empty_repo, namespace: namespace, path: 'environments-project') } + let(:environment) { create(:environment, name: 'production', project: project) } + + render_views + + before(:all) do + clean_frontend_fixtures('environments/metrics') + end + + before(:each) do + sign_in(admin) + end + + it 'environments/metrics/metrics.html.raw' do |example| + get :metrics, + namespace_id: project.namespace, + project_id: project, + id: environment.id + + expect(response).to be_success + store_frontend_fixture(response, example.description) + end +end diff --git a/spec/javascripts/fixtures/environments/metrics.html.haml b/spec/javascripts/fixtures/environments/metrics.html.haml deleted file mode 100644 index 8f806d1aba5..00000000000 --- a/spec/javascripts/fixtures/environments/metrics.html.haml +++ /dev/null @@ -1,62 +0,0 @@ -#js-metrics.prometheus-container{ 'data-has-metrics': "false", 'data-doc-link': '/help/administration/monitoring/prometheus/index.md', 'data-prometheus-integration': '/root/hello-prometheus/services/prometheus/edit', 'data-endpoint': '/test' } - .top-area - .row - .col-sm-6 - %h3.page-title - Metrics for environment - .prometheus-state - .js-getting-started.hidden - .row - .col-md-4.col-md-offset-4.state-svg - %svg - .row - .col-md-6.col-md-offset-3 - %h4.text-center.state-title - Get started with performance monitoring - .row - .col-md-6.col-md-offset-3 - .description-text.text-center.state-description - Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments. Learn more about performance monitoring - .row.state-button-section - .col-md-4.col-md-offset-4.text-center.state-button - %a.btn.btn-success - Configure Prometheus - .js-loading.hidden - .row - .col-md-4.col-md-offset-4.state-svg - %svg - .row - .col-md-6.col-md-offset-3 - %h4.text-center.state-title - Waiting for performance data - .row - .col-md-6.col-md-offset-3 - .description-text.text-center.state-description - Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available. - .row.state-button-section - .col-md-4.col-md-offset-4.text-center.state-button - %a.btn.btn-success - View documentation - .js-unable-to-connect.hidden - .row - .col-md-4.col-md-offset-4.state-svg - %svg - .row - .col-md-6.col-md-offset-3 - %h4.text-center.state-title - Unable to connect to Prometheus server - .row - .col-md-6.col-md-offset-3 - .description-text.text-center.state-description - Ensure connectivity is available from the GitLab server to the Prometheus server - .row.state-button-section - .col-md-4.col-md-offset-4.text-center.state-button - %a.btn.btn-success - View documentation - .prometheus-graphs - .row - .col-sm-12 - %svg.prometheus-graph{ 'graph-type' => 'cpu_values' } - .row - .col-sm-12 - %svg.prometheus-graph{ 'graph-type' => 'memory_values' } diff --git a/spec/javascripts/monitoring/deployments_spec.js b/spec/javascripts/monitoring/deployments_spec.js index 5b88f6d95a3..19bc11d0f24 100644 --- a/spec/javascripts/monitoring/deployments_spec.js +++ b/spec/javascripts/monitoring/deployments_spec.js @@ -4,7 +4,7 @@ import Deployments from '~/monitoring/deployments'; import { prometheusMockData } from './prometheus_mock_data'; describe('Metrics deployments', () => { - const fixtureName = 'static/environments/metrics.html.raw'; + const fixtureName = 'environments/metrics/metrics.html.raw'; let deployment; let prometheusGraph; diff --git a/spec/javascripts/monitoring/prometheus_graph_spec.js b/spec/javascripts/monitoring/prometheus_graph_spec.js index 4b904fc2960..43058088d50 100644 --- a/spec/javascripts/monitoring/prometheus_graph_spec.js +++ b/spec/javascripts/monitoring/prometheus_graph_spec.js @@ -3,7 +3,7 @@ import PrometheusGraph from '~/monitoring/prometheus_graph'; import { prometheusMockData } from './prometheus_mock_data'; describe('PrometheusGraph', () => { - const fixtureName = 'static/environments/metrics.html.raw'; + const fixtureName = 'environments/metrics/metrics.html.raw'; const prometheusGraphContainer = '.prometheus-graph'; const prometheusGraphContents = `${prometheusGraphContainer}[graph-type=cpu_values]`; -- cgit v1.2.1 From 7f22256a63b2c4e6fb1a85beb2ad5420b085247e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Apr 2017 11:50:58 +0100 Subject: Fixed karma loading incorrect path --- spec/javascripts/monitoring/prometheus_graph_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/monitoring/prometheus_graph_spec.js b/spec/javascripts/monitoring/prometheus_graph_spec.js index 43058088d50..25578bf1c6e 100644 --- a/spec/javascripts/monitoring/prometheus_graph_spec.js +++ b/spec/javascripts/monitoring/prometheus_graph_spec.js @@ -77,7 +77,7 @@ describe('PrometheusGraph', () => { }); describe('PrometheusGraphs UX states', () => { - const fixtureName = 'static/environments/metrics.html.raw'; + const fixtureName = 'environments/metrics/metrics.html.raw'; preloadFixtures(fixtureName); beforeEach(() => { -- cgit v1.2.1 From 1dc91f48cfb10d4ed61a06c37e444b72eb7743d3 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Apr 2017 12:33:48 +0100 Subject: Fixed GFM spec failure --- spec/helpers/gitlab_markdown_helper_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 6cf3f86680a..6218305075a 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -113,7 +113,7 @@ describe GitlabMarkdownHelper do it 'replaces commit message with emoji to link' do actual = link_to_gfm(':book:Book', '/foo') expect(actual). - to eq '📖Book' + to eq '📖Book' end end -- cgit v1.2.1 From f5e8d6bded71a0ed827c2f1becb3a3a81ad1f163 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 25 Apr 2017 17:15:41 +0100 Subject: updated to new favicons --- .../ci_favicons/dev/favicon_status_canceled.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/dev/favicon_status_created.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/dev/favicon_status_failed.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/dev/favicon_status_manual.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/dev/favicon_status_pending.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/dev/favicon_status_running.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/dev/favicon_status_skipped.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/dev/favicon_status_success.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/dev/favicon_status_warning.ico | Bin 0 -> 4286 bytes .../images/ci_favicons/favicon_status_canceled.ico | Bin 5430 -> 4286 bytes .../images/ci_favicons/favicon_status_created.ico | Bin 5430 -> 4286 bytes .../images/ci_favicons/favicon_status_failed.ico | Bin 5430 -> 4286 bytes .../images/ci_favicons/favicon_status_manual.ico | Bin 5430 -> 4286 bytes .../images/ci_favicons/favicon_status_pending.ico | Bin 5430 -> 4286 bytes .../images/ci_favicons/favicon_status_running.ico | Bin 5430 -> 4286 bytes .../images/ci_favicons/favicon_status_skipped.ico | Bin 5430 -> 4286 bytes .../images/ci_favicons/favicon_status_success.ico | Bin 5430 -> 4286 bytes .../images/ci_favicons/favicon_status_warning.ico | Bin 5430 -> 4286 bytes app/serializers/status_entity.rb | 5 ++++- 19 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_canceled.ico create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_created.ico create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_failed.ico create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_manual.ico create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_pending.ico create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_running.ico create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_skipped.ico create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_success.ico create mode 100644 app/assets/images/ci_favicons/dev/favicon_status_warning.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_canceled.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_created.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_failed.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_manual.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_pending.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_running.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_skipped.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_success.ico mode change 100755 => 100644 app/assets/images/ci_favicons/favicon_status_warning.ico diff --git a/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico b/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico new file mode 100644 index 00000000000..4af3582b60d Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_created.ico b/app/assets/images/ci_favicons/dev/favicon_status_created.ico new file mode 100644 index 00000000000..13639da2e8a Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_created.ico differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_failed.ico b/app/assets/images/ci_favicons/dev/favicon_status_failed.ico new file mode 100644 index 00000000000..5f0e711b104 Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_failed.ico differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_manual.ico b/app/assets/images/ci_favicons/dev/favicon_status_manual.ico new file mode 100644 index 00000000000..8b1168a1267 Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_manual.ico differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_pending.ico b/app/assets/images/ci_favicons/dev/favicon_status_pending.ico new file mode 100644 index 00000000000..5dfefd4cc5a Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_pending.ico differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_running.ico b/app/assets/images/ci_favicons/dev/favicon_status_running.ico new file mode 100644 index 00000000000..a41539c0e3e Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_running.ico differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico b/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico new file mode 100644 index 00000000000..2c1ae552b93 Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_success.ico b/app/assets/images/ci_favicons/dev/favicon_status_success.ico new file mode 100644 index 00000000000..70f0ca61eca Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_success.ico differ diff --git a/app/assets/images/ci_favicons/dev/favicon_status_warning.ico b/app/assets/images/ci_favicons/dev/favicon_status_warning.ico new file mode 100644 index 00000000000..db289e03eb1 Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_warning.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_canceled.ico b/app/assets/images/ci_favicons/favicon_status_canceled.ico old mode 100755 new mode 100644 index 5a19458f2a2..23adcffff50 Binary files a/app/assets/images/ci_favicons/favicon_status_canceled.ico and b/app/assets/images/ci_favicons/favicon_status_canceled.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_created.ico b/app/assets/images/ci_favicons/favicon_status_created.ico old mode 100755 new mode 100644 index 4dca9640cb3..f9d93b390d8 Binary files a/app/assets/images/ci_favicons/favicon_status_created.ico and b/app/assets/images/ci_favicons/favicon_status_created.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_failed.ico b/app/assets/images/ci_favicons/favicon_status_failed.ico old mode 100755 new mode 100644 index c961ff9a69b..28a22ebf724 Binary files a/app/assets/images/ci_favicons/favicon_status_failed.ico and b/app/assets/images/ci_favicons/favicon_status_failed.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_manual.ico b/app/assets/images/ci_favicons/favicon_status_manual.ico old mode 100755 new mode 100644 index 5fbbc99ea7c..dbbf1abf30c Binary files a/app/assets/images/ci_favicons/favicon_status_manual.ico and b/app/assets/images/ci_favicons/favicon_status_manual.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_pending.ico b/app/assets/images/ci_favicons/favicon_status_pending.ico old mode 100755 new mode 100644 index 8be32dab85a..05962f3f148 Binary files a/app/assets/images/ci_favicons/favicon_status_pending.ico and b/app/assets/images/ci_favicons/favicon_status_pending.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_running.ico b/app/assets/images/ci_favicons/favicon_status_running.ico old mode 100755 new mode 100644 index f328ff1a5ed..7fa3d4d48d4 Binary files a/app/assets/images/ci_favicons/favicon_status_running.ico and b/app/assets/images/ci_favicons/favicon_status_running.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_skipped.ico b/app/assets/images/ci_favicons/favicon_status_skipped.ico old mode 100755 new mode 100644 index b4394e1b4af..b0c26b62068 Binary files a/app/assets/images/ci_favicons/favicon_status_skipped.ico and b/app/assets/images/ci_favicons/favicon_status_skipped.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_success.ico b/app/assets/images/ci_favicons/favicon_status_success.ico old mode 100755 new mode 100644 index 4f436c95242..b150960b5be Binary files a/app/assets/images/ci_favicons/favicon_status_success.ico and b/app/assets/images/ci_favicons/favicon_status_success.ico differ diff --git a/app/assets/images/ci_favicons/favicon_status_warning.ico b/app/assets/images/ci_favicons/favicon_status_warning.ico old mode 100755 new mode 100644 index 805cc20cdec..7e71d71684d Binary files a/app/assets/images/ci_favicons/favicon_status_warning.ico and b/app/assets/images/ci_favicons/favicon_status_warning.ico differ diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb index 944472f3e51..188c3747f18 100644 --- a/app/serializers/status_entity.rb +++ b/app/serializers/status_entity.rb @@ -7,6 +7,9 @@ class StatusEntity < Grape::Entity expose :details_path expose :favicon do |status| - ActionController::Base.helpers.image_path(File.join('ci_favicons', "#{status.favicon}.ico")) + dir = 'ci_favicons' + dir = File.join(dir, 'dev') if Rails.env.development? + + ActionController::Base.helpers.image_path(File.join(dir, "#{status.favicon}.ico")) end end -- cgit v1.2.1 From a14e28abe059cd48b91156227d1ae7f1fbc699fa Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 25 Apr 2017 18:36:05 +0100 Subject: Added test --- spec/serializers/status_entity_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb index c94902dbab8..3964b998084 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -18,6 +18,12 @@ describe StatusEntity do it 'contains status details' do expect(subject).to include :text, :icon, :favicon, :label, :group expect(subject).to include :has_details, :details_path + expect(subject[:favicon]).to eq('/assets/ci_favicons/favicon_status_success.ico') + end + + it 'contains a dev namespaced favicon if dev env' do + allow(Rails.env).to receive(:development?) { true } + expect(entity.as_json[:favicon]).to eq('/assets/ci_favicons/dev/favicon_status_success.ico') end end end -- cgit v1.2.1 From 60a5273d7d49f074a75312642bcf2159b8a30ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Apr 2017 18:31:33 +0200 Subject: Wait for AJAX requests at the JS level in addition to wait for requests at the middleware level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hopefully, this can solve transient failures such as https://gitlab.com/gitlab-org/gitlab-ce/issues/29836. Signed-off-by: Rémy Coutable --- spec/support/wait_for_requests.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb index 0bfa7f72ff8..73da23391ee 100644 --- a/spec/support/wait_for_requests.rb +++ b/spec/support/wait_for_requests.rb @@ -1,11 +1,15 @@ +require_relative './wait_for_ajax' + module WaitForRequests extend self + include WaitForAjax # This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests def wait_for_requests_complete Gitlab::Testing::RequestBlockerMiddleware.block_requests! wait_for('pending AJAX requests complete') do - Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero? + Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero? && + finished_all_ajax_requests? end ensure Gitlab::Testing::RequestBlockerMiddleware.allow_requests! -- cgit v1.2.1 From 11ad7444be81b656e8e2142ae24be0245be60a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 26 Apr 2017 12:40:52 +0200 Subject: Ensure Spinach features with JS waits for requests to complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- features/support/env.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/features/support/env.rb b/features/support/env.rb index 06c804b1db7..92d13bea4b6 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -10,7 +10,7 @@ if ENV['CI'] Knapsack::Adapters::SpinachAdapter.bind end -%w(select2_helper test_env repo_helpers wait_for_ajax sidekiq).each do |f| +%w(select2_helper test_env repo_helpers wait_for_ajax wait_for_requests sidekiq).each do |f| require Rails.root.join('spec', 'support', f) end @@ -30,6 +30,13 @@ Spinach.hooks.before_run do include FactoryGirl::Syntax::Methods end +Spinach.hooks.after_feature do |feature_data| + if feature_data.scenarios.flat_map(&:tags).include?('javascript') + include WaitForRequests + wait_for_requests_complete + end +end + module StdoutReporterWithScenarioLocation # Override the standard reporter to show filename and line number next to each # scenario for easy, focused re-runs -- cgit v1.2.1 From 52d59a4e5108d2ffd6f2bc543ee9aef1a87a8f14 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Apr 2017 15:25:20 +0100 Subject: Load milestone tabs asynchronously --- app/assets/javascripts/milestone.js | 29 +++++++++++----- app/controllers/concerns/milestone_actions.rb | 42 +++++++++++++++++++++++ app/controllers/groups/milestones_controller.rb | 4 ++- app/controllers/projects/milestones_controller.rb | 4 ++- app/helpers/milestones_helper.rb | 24 +++++++++++++ app/views/shared/milestones/_tabs.html.haml | 24 ++++++++----- config/routes/group.rb | 8 ++++- config/routes/project.rb | 3 ++ 8 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 app/controllers/concerns/milestone_actions.rb diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index 38c673e8907..1026f458733 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -81,9 +81,7 @@ }; function Milestone() { - var oldMouseStart; this.bindIssuesSorting(); - this.bindMergeRequestSorting(); this.bindTabsSwitching(); } @@ -100,13 +98,14 @@ }; Milestone.prototype.bindTabsSwitching = function() { - return $('a[data-toggle="tab"]').on('show.bs.tab', function(e) { - var currentTabClass, previousTabClass; - currentTabClass = $(e.target).data('show'); - previousTabClass = $(e.relatedTarget).data('show'); - $(previousTabClass).hide(); - $(currentTabClass).removeClass('hidden'); - return $(currentTabClass).show(); + return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => { + const $target = $(e.target); + const endpoint = $target.data('endpoint'); + + if (endpoint && !$target.hasClass('is-loaded')) { + this.loadMergeRequests($target.attr('href'), endpoint) + .done(() => $target.addClass('is-loaded')); + } }); }; @@ -169,6 +168,18 @@ }); }; + Milestone.prototype.loadMergeRequests = function(elId, url) { + return $.ajax({ + url, + dataType: 'JSON', + }) + .fail(() => new Flash('Error loading merge requests')) + .done((data) => { + $(elId).html(data.html); + this.bindMergeRequestSorting(); + }); + }; + return Milestone; })(); }).call(window); diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb new file mode 100644 index 00000000000..c28d08201e0 --- /dev/null +++ b/app/controllers/concerns/milestone_actions.rb @@ -0,0 +1,42 @@ +module MilestoneActions + extend ActiveSupport::Concern + + def merge_requests + respond_to do |format| + format.json do + render json: tabs_json("shared/milestones/_merge_requests_tab", { + merge_requests: @milestone.merge_requests, + show_project_name: true + }) + end + end + end + + def participants + respond_to do |format| + format.json do + render json: tabs_json("shared/milestones/_participants_tab", { + users: @milestone.participants + }) + end + end + end + + def labels + respond_to do |format| + format.json do + render json: tabs_json("shared/milestones/_labels_tab", { + labels: @milestone.labels + }) + end + end + end + + private + + def tabs_json(partial, data = {}) + { + html: view_to_html_string(partial, data) + } + end +end diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 43102596201..e52fa766044 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -1,6 +1,8 @@ class Groups::MilestonesController < Groups::ApplicationController + include MilestoneActions + before_action :group_projects - before_action :milestone, only: [:show, :update] + before_action :milestone, only: [:show, :update, :merge_requests, :participants, :labels] before_action :authorize_admin_milestones!, only: [:new, :create, :update] def index diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index d0dd524c484..5c270501a24 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -1,6 +1,8 @@ class Projects::MilestonesController < Projects::ApplicationController + include MilestoneActions + before_action :module_enabled - before_action :milestone, only: [:edit, :update, :destroy, :show, :sort_issues, :sort_merge_requests] + before_action :milestone, only: [:edit, :update, :destroy, :show, :sort_issues, :sort_merge_requests, :merge_requests, :participants, :labels] # Allow read any milestone before_action :authorize_read_milestone! diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index c9e70faa52e..d46b010f535 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -115,4 +115,28 @@ module MilestonesHelper end end end + + def milestone_merge_request_path(milestone) + if @project + merge_requests_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) + elsif @group + merge_requests_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json) + end + end + + def milestone_participants_path(milestone) + if @project + participants_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) + elsif @group + participants_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json) + end + end + + def milestone_labels_path(milestone) + if @project + labels_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) + elsif @group + labels_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json) + end + end end diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml index 9a4502873ef..bde2db756ce 100644 --- a/app/views/shared/milestones/_tabs.html.haml +++ b/app/views/shared/milestones/_tabs.html.haml @@ -8,20 +8,20 @@ Issues %span.badge= milestone.issues_visible_to_user(current_user).size %li - = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-show' => '.tab-merge-requests-buttons' do + = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-endpoint': milestone_merge_request_path(milestone) do Merge Requests %span.badge= milestone.merge_requests.size - else %li.active - = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-show' => '.tab-merge-requests-buttons' do + = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-endpoint': milestone_merge_request_path(milestone) do Merge Requests %span.badge= milestone.merge_requests.size %li - = link_to '#tab-participants', 'data-toggle' => 'tab' do + = link_to '#tab-participants', 'data-toggle' => 'tab', 'data-endpoint': milestone_participants_path(milestone) do Participants %span.badge= milestone.participants.count %li - = link_to '#tab-labels', 'data-toggle' => 'tab' do + = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-endpoint': milestone_labels_path(milestone) do Labels %span.badge= milestone.labels.count @@ -33,11 +33,19 @@ .tab-pane.active#tab-issues = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user).include_associations, show_project_name: show_project_name, show_full_project_name: show_full_project_name .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', merge_requests: milestone.merge_requests, show_project_name: show_project_name, show_full_project_name: show_full_project_name + -# loaded async + .text-center.prepend-top-default + = icon('spin spinner 2x') - else .tab-pane.active#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', merge_requests: milestone.merge_requests, show_project_name: show_project_name, show_full_project_name: show_full_project_name + -# loaded async + .text-center.prepend-top-default + = icon('spin spinner 2x') .tab-pane#tab-participants - = render 'shared/milestones/participants_tab', users: milestone.participants + -# loaded async + .text-center.prepend-top-default + = icon('spin spinner 2x') .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: milestone.labels + -# loaded async + .text-center.prepend-top-default + = icon('spin spinner 2x') diff --git a/config/routes/group.rb b/config/routes/group.rb index 73f69d76995..7b29e0e807c 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -10,7 +10,13 @@ scope(path: 'groups/*group_id', end resource :avatar, only: [:destroy] - resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create] + resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create] do + member do + get :merge_requests + get :participants + get :labels + end + end resources :labels, except: [:show] do post :toggle_subscription, on: :member diff --git a/config/routes/project.rb b/config/routes/project.rb index fa92202c1ea..e0f40e74500 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -205,6 +205,9 @@ constraints(ProjectUrlConstrainer.new) do member do put :sort_issues put :sort_merge_requests + get :merge_requests + get :participants + get :labels end end -- cgit v1.2.1 From 79c7188a870bbbf4fba294a8d88530fcfe2403be Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Apr 2017 16:06:17 +0100 Subject: Change the hash when changing tab This allows the tab to be loaded by default when the page loads & the hash is present --- app/assets/javascripts/milestone.js | 54 +++++++++++++++------- app/helpers/milestones_helper.rb | 6 +-- app/views/shared/milestones/_tab_loading.html.haml | 2 + app/views/shared/milestones/_tabs.html.haml | 22 ++++----- changelogs/unreleased/async-milestone-tabs.yml | 4 ++ 5 files changed, 55 insertions(+), 33 deletions(-) create mode 100644 app/views/shared/milestones/_tab_loading.html.haml create mode 100644 changelogs/unreleased/async-milestone-tabs.yml diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index 1026f458733..cd6f94e1365 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -21,7 +21,7 @@ Milestone.sortIssues = function(data) { var sort_issues_url; - sort_issues_url = location.href + "/sort_issues"; + sort_issues_url = location.pathname + "/sort_issues"; return $.ajax({ type: "PUT", url: sort_issues_url, @@ -38,7 +38,7 @@ Milestone.sortMergeRequests = function(data) { var sort_mr_url; - sort_mr_url = location.href + "/sort_merge_requests"; + sort_mr_url = location.pathname + "/sort_merge_requests"; return $.ajax({ type: "PUT", url: sort_mr_url, @@ -83,6 +83,12 @@ function Milestone() { this.bindIssuesSorting(); this.bindTabsSwitching(); + + this.loadInitialTab(); + + // Load merge request tab if it is active + // merge request tab is active based on different conditions in the backend + this.loadTab($('.js-milestone-tabs .active a')); } Milestone.prototype.bindIssuesSorting = function() { @@ -100,12 +106,9 @@ Milestone.prototype.bindTabsSwitching = function() { return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => { const $target = $(e.target); - const endpoint = $target.data('endpoint'); - if (endpoint && !$target.hasClass('is-loaded')) { - this.loadMergeRequests($target.attr('href'), endpoint) - .done(() => $target.addClass('is-loaded')); - } + location.hash = $target.attr('href'); + this.loadTab($target); }); }; @@ -168,16 +171,33 @@ }); }; - Milestone.prototype.loadMergeRequests = function(elId, url) { - return $.ajax({ - url, - dataType: 'JSON', - }) - .fail(() => new Flash('Error loading merge requests')) - .done((data) => { - $(elId).html(data.html); - this.bindMergeRequestSorting(); - }); + Milestone.prototype.loadInitialTab = function() { + const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`); + + if ($target.length) { + $target.tab('show'); + } + }; + + Milestone.prototype.loadTab = function($target) { + const endpoint = $target.data('endpoint'); + const tabElId = $target.attr('href'); + + if (endpoint && !$target.hasClass('is-loaded')) { + $.ajax({ + url: endpoint, + dataType: 'JSON', + }) + .fail(() => new Flash('Error loading milestone tab')) + .done((data) => { + $(tabElId).html(data.html); + $target.addClass('is-loaded'); + + if (tabElId === '#tab-merge-requests') { + this.bindMergeRequestSorting(); + } + }); + } }; return Milestone; diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index d46b010f535..c515774140c 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -116,7 +116,7 @@ module MilestonesHelper end end - def milestone_merge_request_path(milestone) + def milestone_merge_request_tab_path(milestone) if @project merge_requests_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) elsif @group @@ -124,7 +124,7 @@ module MilestonesHelper end end - def milestone_participants_path(milestone) + def milestone_participants_tab_path(milestone) if @project participants_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) elsif @group @@ -132,7 +132,7 @@ module MilestonesHelper end end - def milestone_labels_path(milestone) + def milestone_labels_tab_path(milestone) if @project labels_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json) elsif @group diff --git a/app/views/shared/milestones/_tab_loading.html.haml b/app/views/shared/milestones/_tab_loading.html.haml new file mode 100644 index 00000000000..68458c2d0aa --- /dev/null +++ b/app/views/shared/milestones/_tab_loading.html.haml @@ -0,0 +1,2 @@ +.text-center.prepend-top-default + = icon('spin spinner 2x', 'aria-hidden': 'true', 'aria-label': 'Loading tab content') diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml index bde2db756ce..6717d59f033 100644 --- a/app/views/shared/milestones/_tabs.html.haml +++ b/app/views/shared/milestones/_tabs.html.haml @@ -1,27 +1,27 @@ .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller .fade-left= icon('angle-left') .fade-right= icon('angle-right') - %ul.nav-links.scrolling-tabs + %ul.nav-links.scrolling-tabs.js-milestone-tabs - if milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project) %li.active = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do Issues %span.badge= milestone.issues_visible_to_user(current_user).size %li - = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-endpoint': milestone_merge_request_path(milestone) do + = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-endpoint': milestone_merge_request_tab_path(milestone) do Merge Requests %span.badge= milestone.merge_requests.size - else %li.active - = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-endpoint': milestone_merge_request_path(milestone) do + = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-endpoint': milestone_merge_request_tab_path(milestone) do Merge Requests %span.badge= milestone.merge_requests.size %li - = link_to '#tab-participants', 'data-toggle' => 'tab', 'data-endpoint': milestone_participants_path(milestone) do + = link_to '#tab-participants', 'data-toggle' => 'tab', 'data-endpoint': milestone_participants_tab_path(milestone) do Participants %span.badge= milestone.participants.count %li - = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-endpoint': milestone_labels_path(milestone) do + = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-endpoint': milestone_labels_tab_path(milestone) do Labels %span.badge= milestone.labels.count @@ -34,18 +34,14 @@ = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user).include_associations, show_project_name: show_project_name, show_full_project_name: show_full_project_name .tab-pane#tab-merge-requests -# loaded async - .text-center.prepend-top-default - = icon('spin spinner 2x') + = render "shared/milestones/tab_loading" - else .tab-pane.active#tab-merge-requests -# loaded async - .text-center.prepend-top-default - = icon('spin spinner 2x') + = render "shared/milestones/tab_loading" .tab-pane#tab-participants -# loaded async - .text-center.prepend-top-default - = icon('spin spinner 2x') + = render "shared/milestones/tab_loading" .tab-pane#tab-labels -# loaded async - .text-center.prepend-top-default - = icon('spin spinner 2x') + = render "shared/milestones/tab_loading" diff --git a/changelogs/unreleased/async-milestone-tabs.yml b/changelogs/unreleased/async-milestone-tabs.yml new file mode 100644 index 00000000000..c199a95610c --- /dev/null +++ b/changelogs/unreleased/async-milestone-tabs.yml @@ -0,0 +1,4 @@ +--- +title: Load milestone tabs asynchronously to increase initial load performance +merge_request: +author: -- cgit v1.2.1 From 8efa88a306f599f131100296f2b8bb7ae1590741 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Apr 2017 17:41:13 +0100 Subject: Fixed feature spec not waiting for ajax request to finish Fixed 404 in project milestones when not logged in --- app/controllers/projects/milestones_controller.rb | 2 +- spec/features/milestones/milestones_spec.rb | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 5c270501a24..c56bce19eee 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -8,7 +8,7 @@ class Projects::MilestonesController < Projects::ApplicationController before_action :authorize_read_milestone! # Allow admin milestone - before_action :authorize_admin_milestone!, except: [:index, :show] + before_action :authorize_admin_milestone!, except: [:index, :show, :merge_requests, :participants, :labels] respond_to :html diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb index 50d7ca39045..9eec3d7f270 100644 --- a/spec/features/milestones/milestones_spec.rb +++ b/spec/features/milestones/milestones_spec.rb @@ -86,6 +86,9 @@ describe 'Milestone draggable', feature: true, js: true do visit namespace_project_milestone_path(project.namespace, project, milestone) page.find("a[href='#tab-merge-requests']").click + + wait_for_ajax + scroll_into_view('.milestone-content') drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1) -- cgit v1.2.1 From 3c077fad8367264bed3b33fafa537d5c75c4c249 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Apr 2017 17:45:11 +0100 Subject: Fixed tabs loading the ajax request twice --- app/assets/javascripts/milestone.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index cd6f94e1365..cd2bd976160 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -84,11 +84,11 @@ this.bindIssuesSorting(); this.bindTabsSwitching(); - this.loadInitialTab(); - // Load merge request tab if it is active // merge request tab is active based on different conditions in the backend this.loadTab($('.js-milestone-tabs .active a')); + + this.loadInitialTab(); } Milestone.prototype.bindIssuesSorting = function() { -- cgit v1.2.1 From 45e5d12122ac04d7558c84e47003fc8c85a9dddd Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 26 Apr 2017 08:37:50 +0100 Subject: Fixed spinach tests Removed a spinach test as it wasn't actually testing what it said it should be --- features/group/milestones.feature | 1 + features/project/milestone.feature | 8 -------- features/steps/group/milestones.rb | 3 +++ features/steps/project/project_milestone.rb | 3 +++ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/features/group/milestones.feature b/features/group/milestones.feature index d6c05df9840..1c1539b3e12 100644 --- a/features/group/milestones.feature +++ b/features/group/milestones.feature @@ -38,6 +38,7 @@ Feature: Group Milestones And I should see the "feature" label And I should see the project name in the Issue row + @javascript Scenario: I should see the Labels tab Given Group has projects with milestones When I visit group "Owned" page diff --git a/features/project/milestone.feature b/features/project/milestone.feature index 713f0f3b979..5e7b211fa27 100644 --- a/features/project/milestone.feature +++ b/features/project/milestone.feature @@ -7,14 +7,6 @@ Feature: Project Milestone And milestone has issue "Bugfix1" with labels: "bug", "feature" And milestone has issue "Bugfix2" with labels: "bug", "enhancement" - - @javascript - Scenario: Listing issues from issues tab - Given I visit project "Shop" milestones page - And I click link "v2.2" - Then I should see the labels "bug", "enhancement" and "feature" - And I should see the "bug" label listed only once - @javascript Scenario: Listing labels from labels tab Given I visit project "Shop" milestones page diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index f8f5e3f2382..49fcd6f1201 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -1,4 +1,5 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps + include WaitForAjax include SharedAuthentication include SharedPaths include SharedGroup @@ -90,6 +91,8 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end step 'I should see the list of labels' do + wait_for_ajax + page.within('#tab-labels') do expect(page).to have_content 'bug' expect(page).to have_content 'feature' diff --git a/features/steps/project/project_milestone.rb b/features/steps/project/project_milestone.rb index 1864b3a2b52..dc1190b7eea 100644 --- a/features/steps/project/project_milestone.rb +++ b/features/steps/project/project_milestone.rb @@ -2,6 +2,7 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps include SharedAuthentication include SharedProject include SharedPaths + include WaitForAjax step 'milestone has issue "Bugfix1" with labels: "bug", "feature"' do project = Project.find_by(name: "Shop") @@ -34,6 +35,8 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps end step 'I should see the labels "bug", "enhancement" and "feature"' do + wait_for_ajax + page.within('#tab-issues') do expect(page).to have_content 'bug' expect(page).to have_content 'enhancement' -- cgit v1.2.1 From 480fcb5533e59151a6e9ae11baef59ebab64cc6c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 26 Apr 2017 11:32:21 +0100 Subject: Added controller specs --- app/controllers/concerns/milestone_actions.rb | 11 ++++ .../groups/milestones_controller_spec.rb | 12 ++++ .../projects/milestones_controller_spec.rb | 5 ++ spec/support/milestone_tabs_examples.rb | 67 ++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 spec/support/milestone_tabs_examples.rb diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index c28d08201e0..2cc7e15c27d 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -3,6 +3,7 @@ module MilestoneActions def merge_requests respond_to do |format| + format.html { redirect_to milestone_path } format.json do render json: tabs_json("shared/milestones/_merge_requests_tab", { merge_requests: @milestone.merge_requests, @@ -14,6 +15,7 @@ module MilestoneActions def participants respond_to do |format| + format.html { redirect_to milestone_path } format.json do render json: tabs_json("shared/milestones/_participants_tab", { users: @milestone.participants @@ -24,6 +26,7 @@ module MilestoneActions def labels respond_to do |format| + format.html { redirect_to milestone_path } format.json do render json: tabs_json("shared/milestones/_labels_tab", { labels: @milestone.labels @@ -39,4 +42,12 @@ module MilestoneActions html: view_to_html_string(partial, data) } end + + def milestone_path + if @project + namespace_project_milestone_path(@project.namespace, @project, @milestone) + else + group_milestone_path(@group, @milestone.safe_title, title: @milestone.title) + end + end end diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb index 6e4b5f78e33..7cf2996ffd0 100644 --- a/spec/controllers/groups/milestones_controller_spec.rb +++ b/spec/controllers/groups/milestones_controller_spec.rb @@ -6,6 +6,16 @@ describe Groups::MilestonesController do let(:project2) { create(:empty_project, group: group) } let(:user) { create(:user) } let(:title) { '肯定不是中文的问题' } + let(:milestone) do + project_milestone = create(:milestone, project: project) + + GroupMilestone.build( + group, + [project], + project_milestone.title + ) + end + let(:milestone_path) { group_milestone_path(group, milestone.safe_title, title: milestone.title) } before do sign_in(user) @@ -14,6 +24,8 @@ describe Groups::MilestonesController do controller.instance_variable_set(:@group, group) end + it_behaves_like 'milestone tabs' + describe "#create" do it "creates group milestone with Chinese title" do post :create, diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 47e61c3cea8..6f500468f95 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -7,6 +7,7 @@ describe Projects::MilestonesController do let(:issue) { create(:issue, project: project, milestone: milestone) } let!(:label) { create(:label, project: project, title: 'Issue Label', issues: [issue]) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) } + let(:milestone_path) { namespace_project_milestone_path } before do sign_in(user) @@ -14,6 +15,8 @@ describe Projects::MilestonesController do controller.instance_variable_set(:@project, project) end + it_behaves_like 'milestone tabs' + describe "#show" do render_views @@ -49,4 +52,6 @@ describe Projects::MilestonesController do expect(last_note).to eq('removed milestone') end end + + end diff --git a/spec/support/milestone_tabs_examples.rb b/spec/support/milestone_tabs_examples.rb new file mode 100644 index 00000000000..c48ecb1672e --- /dev/null +++ b/spec/support/milestone_tabs_examples.rb @@ -0,0 +1,67 @@ +shared_examples 'milestone tabs' do + def go(path, extra_params = {}) + params = if milestone.is_a?(GlobalMilestone) + { group_id: group.id, id: milestone.safe_title, title: milestone.title } + else + { namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid } + end + + get path, params.merge(extra_params) + end + describe '#merge_requests' do + context 'as html' do + before { go(:merge_requests, format: 'html') } + + it 'redirects to milestone#show' do + expect(response).to redirect_to(milestone_path) + end + end + + context 'as json' do + before { go(:merge_requests, format: 'json') } + + it 'renders the merge requests tab template to a string' do + expect(response).to render_template('shared/milestones/_merge_requests_tab') + expect(json_response).to have_key('html') + end + end + end + + describe '#participants' do + context 'as html' do + before { go(:participants, format: 'html') } + + it 'redirects to milestone#show' do + expect(response).to redirect_to(milestone_path) + end + end + + context 'as json' do + before { go(:participants, format: 'json') } + + it 'renders the participants tab template to a string' do + expect(response).to render_template('shared/milestones/_participants_tab') + expect(json_response).to have_key('html') + end + end + end + + describe '#labels' do + context 'as html' do + before { go(:labels, format: 'html') } + + it 'redirects to milestone#show' do + expect(response).to redirect_to(milestone_path) + end + end + + context 'as json' do + before { go(:labels, format: 'json') } + + it 'renders the labels tab template to a string' do + expect(response).to render_template('shared/milestones/_labels_tab') + expect(json_response).to have_key('html') + end + end + end +end -- cgit v1.2.1 From 4b812d979eafa244c9e6b014448bf09bf1b458fd Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 26 Apr 2017 12:22:09 +0100 Subject: Fixed failing specs --- app/controllers/concerns/milestone_actions.rb | 8 ++++---- spec/controllers/projects/milestones_controller_spec.rb | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index 2cc7e15c27d..3e2a0fe4f8b 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -3,7 +3,7 @@ module MilestoneActions def merge_requests respond_to do |format| - format.html { redirect_to milestone_path } + format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_merge_requests_tab", { merge_requests: @milestone.merge_requests, @@ -15,7 +15,7 @@ module MilestoneActions def participants respond_to do |format| - format.html { redirect_to milestone_path } + format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_participants_tab", { users: @milestone.participants @@ -26,7 +26,7 @@ module MilestoneActions def labels respond_to do |format| - format.html { redirect_to milestone_path } + format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_labels_tab", { labels: @milestone.labels @@ -43,7 +43,7 @@ module MilestoneActions } end - def milestone_path + def milestone_redirect_path if @project namespace_project_milestone_path(@project.namespace, @project, @milestone) else diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 6f500468f95..84a61b2784e 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -52,6 +52,4 @@ describe Projects::MilestonesController do expect(last_note).to eq('removed milestone') end end - - end -- cgit v1.2.1 From ccac05dd90cb5cfa9abbeb11a50f953541eb83bb Mon Sep 17 00:00:00 2001 From: mhasbini Date: Thu, 27 Apr 2017 01:04:07 +0300 Subject: Fix 404 when upstream has disabled merge requests --- app/helpers/merge_requests_helper.rb | 6 ++- app/models/merge_request.rb | 7 ++++ app/models/project.rb | 8 ++++ app/services/merge_requests/build_service.rb | 2 +- .../projects/merge_requests/_new_compare.html.haml | 2 +- changelogs/unreleased/26488-target-disabled-mr.yml | 4 ++ lib/api/merge_requests.rb | 2 + lib/api/v3/merge_requests.rb | 2 + spec/helpers/merge_requests_helper_spec.rb | 46 +++++++++++++++++++++- spec/requests/api/merge_requests_spec.rb | 13 ++++++ spec/requests/api/v3/merge_requests_spec.rb | 13 ++++++ spec/services/merge_requests/build_service_spec.rb | 10 +++++ 12 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/26488-target-disabled-mr.yml diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index e347f61fb8d..2614cdfe90e 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,6 +1,6 @@ module MergeRequestsHelper def new_mr_path_from_push_event(event) - target_project = event.project.forked_from_project || event.project + target_project = event.project.default_merge_request_target new_namespace_project_merge_request_path( event.project.namespace, event.project, @@ -127,6 +127,10 @@ module MergeRequestsHelper end end + def target_projects(project) + [project, project.default_merge_request_target].uniq + end + def merge_request_button_visibility(merge_request, closed) return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork? end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 9d2288c311e..365fa4f1e70 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -100,6 +100,7 @@ class MergeRequest < ActiveRecord::Base validates :merge_user, presence: true, if: :merge_when_pipeline_succeeds?, unless: :importing? validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?] validate :validate_fork, unless: :closed_without_fork? + validate :validate_target_project, on: :create scope :by_source_or_target_branch, ->(branch_name) do where("source_branch = :branch OR target_branch = :branch", branch: branch_name) @@ -330,6 +331,12 @@ class MergeRequest < ActiveRecord::Base end end + def validate_target_project + return true if target_project.merge_requests_enabled? + + errors.add :base, 'Target project has disabled merge requests' + end + def validate_fork return true unless target_project && source_project return true if target_project == source_project diff --git a/app/models/project.rb b/app/models/project.rb index c7dc562c238..9d64e5d406d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1314,6 +1314,14 @@ class Project < ActiveRecord::Base namespace_id_changed? end + def default_merge_request_target + if forked_from_project&.merge_requests_enabled? + forked_from_project + else + self + end + end + alias_method :name_with_namespace, :full_name alias_method :human_name, :full_name alias_method :path_with_namespace, :full_path diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index d45da5180e1..bc0e7ad4e39 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -28,7 +28,7 @@ module MergeRequests def find_target_project return target_project if target_project.present? && can?(current_user, :read_project, target_project) - project.forked_from_project || project + project.default_merge_request_target end def find_target_branch diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index 8d134aaac67..9cf24e10842 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -38,7 +38,7 @@ .panel-heading Target branch .panel-body.clearfix - - projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project] + - projects = target_projects(@project) .merge-request-select.dropdown = f.hidden_field :target_project_id = dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" } diff --git a/changelogs/unreleased/26488-target-disabled-mr.yml b/changelogs/unreleased/26488-target-disabled-mr.yml new file mode 100644 index 00000000000..02058481ccf --- /dev/null +++ b/changelogs/unreleased/26488-target-disabled-mr.yml @@ -0,0 +1,4 @@ +--- +title: Disallow merge requests from fork when source project have disabled merge requests +merge_request: +author: mhasbini diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index e5793fbc5cb..710deba5ae3 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -20,6 +20,8 @@ module API error!(errors[:validate_fork], 422) elsif errors[:validate_branches].any? conflict!(errors[:validate_branches]) + elsif errors[:base].any? + error!(errors[:base], 422) end render_api_error!(errors, 400) diff --git a/lib/api/v3/merge_requests.rb b/lib/api/v3/merge_requests.rb index 3077240e650..1616142a619 100644 --- a/lib/api/v3/merge_requests.rb +++ b/lib/api/v3/merge_requests.rb @@ -23,6 +23,8 @@ module API error!(errors[:validate_fork], 422) elsif errors[:validate_branches].any? conflict!(errors[:validate_branches]) + elsif errors[:base].any? + error!(errors[:base], 422) end render_api_error!(errors, 400) diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index e9037749ef2..10681af5f7e 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -64,7 +64,7 @@ describe MergeRequestsHelper do it do @project = project - + is_expected.to eq("#1, #2, and #{other_project.namespace.path}/#{other_project.path}#3") end end @@ -149,6 +149,50 @@ describe MergeRequestsHelper do end end + describe '#target_projects' do + let(:project) { create(:empty_project) } + let(:fork_project) { create(:empty_project, forked_from_project: project) } + + context 'when target project has enabled merge requests' do + it 'returns the forked_from project' do + expect(target_projects(fork_project)).to contain_exactly(project, fork_project) + end + end + + context 'when target project has disabled merge requests' do + it 'returns the forked project' do + project.project_feature.update(merge_requests_access_level: 0) + + expect(target_projects(fork_project)).to contain_exactly(fork_project) + end + end + end + + describe '#new_mr_path_from_push_event' do + subject(:url_params) { URI.decode_www_form(new_mr_path_from_push_event(event)).to_h } + let(:user) { create(:user) } + let(:project) { create(:empty_project, creator: user) } + let(:fork_project) { create(:project, forked_from_project: project, creator: user) } + let(:event) do + push_data = Gitlab::DataBuilder::Push.build_sample(fork_project, user) + create(:event, :pushed, project: fork_project, target: fork_project, author: user, data: push_data) + end + + context 'when target project has enabled merge requests' do + it 'returns link to create merge request on source project' do + expect(url_params['merge_request[target_project_id]'].to_i).to eq(project.id) + end + end + + context 'when target project has disabled merge requests' do + it 'returns link to create merge request on forked project' do + project.project_feature.update(merge_requests_access_level: 0) + + expect(url_params['merge_request[target_project_id]'].to_i).to eq(fork_project.id) + end + end + end + describe '#mr_issues_mentioned_but_not_closing' do let(:user_1) { create(:user) } let(:user_2) { create(:user) } diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index c4bff1647b5..16e5efb2f5b 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -434,6 +434,19 @@ describe API::MergeRequests do expect(json_response['title']).to eq('Test merge_request') end + it 'returns 422 when target project has disabled merge requests' do + project.project_feature.update(merge_requests_access_level: 0) + + post api("/projects/#{fork_project.id}/merge_requests", user2), + title: 'Test', + target_branch: 'master', + source_branch: 'markdown', + author: user2, + target_project_id: project.id + + expect(response).to have_http_status(422) + end + it "returns 400 when source_branch is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb index 6c2950a6e6f..f6ff96be566 100644 --- a/spec/requests/api/v3/merge_requests_spec.rb +++ b/spec/requests/api/v3/merge_requests_spec.rb @@ -338,6 +338,19 @@ describe API::MergeRequests do expect(json_response['title']).to eq('Test merge_request') end + it "returns 422 when target project has disabled merge requests" do + project.project_feature.update(merge_requests_access_level: 0) + + post v3_api("/projects/#{fork_project.id}/merge_requests", user2), + title: 'Test', + target_branch: "master", + source_branch: 'markdown', + author: user2, + target_project_id: project.id + + expect(response).to have_http_status(422) + end + it "returns 400 when source_branch is missing" do post v3_api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index be9f9ea2dec..6f9d1208b1d 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -261,6 +261,16 @@ describe MergeRequests::BuildService, services: true do end end + context 'upstream project has disabled merge requests' do + let(:upstream_project) { create(:empty_project, :merge_requests_disabled) } + let(:project) { create(:empty_project, forked_from_project: upstream_project) } + let(:commits) { Commit.decorate([commit_1], project) } + + it 'sets target project correctly' do + expect(merge_request.target_project).to eq(project) + end + end + context 'target_project is set and accessible by current_user' do let(:target_project) { create(:project, :public, :repository)} let(:commits) { Commit.decorate([commit_1], project) } -- cgit v1.2.1 From 6236705ef6413fdc58a1d5514fc34d1f481d654f Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Thu, 27 Apr 2017 02:03:29 +0000 Subject: Gitlab -> GitLab --- doc/administration/integration/terminal.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md index 3b5ee86b68b..91e844c7b42 100644 --- a/doc/administration/integration/terminal.md +++ b/doc/administration/integration/terminal.md @@ -32,7 +32,7 @@ In brief: As web terminals use WebSockets, every HTTP/HTTPS reverse proxy in front of Workhorse needs to be configured to pass the `Connection` and `Upgrade` headers -through to the next one in the chain. If you installed Gitlab using Omnibus, or +through to the next one in the chain. If you installed GitLab using Omnibus, or from source, starting with GitLab 8.15, this should be done by the default configuration, so there's no need for you to do anything. @@ -58,7 +58,7 @@ document for more details. If you'd like to disable web terminal support in GitLab, just stop passing the `Connection` and `Upgrade` hop-by-hop headers in the *first* HTTP reverse proxy in the chain. For most users, this will be the NGINX server bundled with -Omnibus Gitlab, in which case, you need to: +Omnibus GitLab, in which case, you need to: * Find the `nginx['proxy_set_headers']` section of your `gitlab.rb` file * Ensure the whole block is uncommented, and then comment out or remove the -- cgit v1.2.1 From 5efd294d77e9eb7366fe71fdfd49314376cb7683 Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Thu, 27 Apr 2017 02:06:08 +0000 Subject: use TCP or HTTPS protocol for port 443 on load balancer --- doc/administration/high_availability/load_balancer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/high_availability/load_balancer.md b/doc/administration/high_availability/load_balancer.md index 3245988fc14..2f8037c454b 100644 --- a/doc/administration/high_availability/load_balancer.md +++ b/doc/administration/high_availability/load_balancer.md @@ -13,7 +13,7 @@ you need to use with GitLab. | LB Port | Backend Port | Protocol | | ------- | ------------ | --------------- | | 80 | 80 | HTTP [^1] | -| 443 | 443 | HTTPS [^1] [^2] | +| 443 | 443 | TCP or HTTPS [^1] [^2] | | 22 | 22 | TCP | ## GitLab Pages Ports -- cgit v1.2.1 From e985d5a56f14dc78ba0b6f0d369035a41b080c7b Mon Sep 17 00:00:00 2001 From: menway Date: Tue, 28 Mar 2017 15:21:57 +0000 Subject: Change to correct directory in update instructions [ci skip] --- doc/update/8.10-to-8.11.md | 2 ++ doc/update/8.11-to-8.12.md | 2 ++ doc/update/8.12-to-8.13.md | 2 ++ doc/update/8.13-to-8.14.md | 2 ++ 4 files changed, 8 insertions(+) diff --git a/doc/update/8.10-to-8.11.md b/doc/update/8.10-to-8.11.md index e5e3cd395df..e538983e603 100644 --- a/doc/update/8.10-to-8.11.md +++ b/doc/update/8.10-to-8.11.md @@ -49,6 +49,8 @@ sudo gem install bundler --no-ri --no-rdoc ### 4. Get latest code ```bash +cd /home/git/gitlab + sudo -u git -H git fetch --all sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically ``` diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md index d6b3b0ffa5a..604166beb56 100644 --- a/doc/update/8.11-to-8.12.md +++ b/doc/update/8.11-to-8.12.md @@ -49,6 +49,8 @@ sudo gem install bundler --no-ri --no-rdoc ### 4. Get latest code ```bash +cd /home/git/gitlab + sudo -u git -H git fetch --all sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically ``` diff --git a/doc/update/8.12-to-8.13.md b/doc/update/8.12-to-8.13.md index ed0e668d854..d83965131f5 100644 --- a/doc/update/8.12-to-8.13.md +++ b/doc/update/8.12-to-8.13.md @@ -49,6 +49,8 @@ sudo gem install bundler --no-ri --no-rdoc ### 4. Get latest code ```bash +cd /home/git/gitlab + sudo -u git -H git fetch --all sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically ``` diff --git a/doc/update/8.13-to-8.14.md b/doc/update/8.13-to-8.14.md index aa1c659717e..aaadcec8ac0 100644 --- a/doc/update/8.13-to-8.14.md +++ b/doc/update/8.13-to-8.14.md @@ -49,6 +49,8 @@ sudo gem install bundler --no-ri --no-rdoc ### 4. Get latest code ```bash +cd /home/git/gitlab + sudo -u git -H git fetch --all sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically ``` -- cgit v1.2.1 From a922d90414fea5b05152717a72f169c0d9ef7d09 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Thu, 27 Apr 2017 22:47:50 -0400 Subject: Add Vue with async deploy keys --- app/assets/javascripts/dispatcher.js | 3 + .../javascripts/settings/settings_repository.js | 92 ++++++++++++++++++++++ app/views/projects/deploy_keys/_index.html.haml | 33 +++----- 3 files changed, 107 insertions(+), 21 deletions(-) create mode 100644 app/assets/javascripts/settings/settings_repository.js diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index b20673cd03c..2300daf0727 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -48,6 +48,7 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion'; import UserCallout from './user_callout'; import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags'; import ShortcutsWiki from './shortcuts_wiki'; +import SettingsDeployKeys from './settings/settings_repository'; const ShortcutsBlob = require('./shortcuts_blob'); @@ -343,6 +344,8 @@ const ShortcutsBlob = require('./shortcuts_blob'); // Initialize Protected Tag Settings new ProtectedTagCreate(); new ProtectedTagEditList(); + // Initialize deploy key ajax call + new SettingsDeployKeys(); break; case 'projects:ci_cd:show': new gl.ProjectVariables(); diff --git a/app/assets/javascripts/settings/settings_repository.js b/app/assets/javascripts/settings/settings_repository.js new file mode 100644 index 00000000000..4c6f769f533 --- /dev/null +++ b/app/assets/javascripts/settings/settings_repository.js @@ -0,0 +1,92 @@ +/* eslint-disable no-new */ +import Vue from 'vue'; +import VueResource from 'vue-resource'; + +Vue.use(VueResource); + +export default class SettingsDeployKeys { + constructor() { + this.initVue(); + } + + deployKeyRowTemplate() { + return ` +
    • + +
      + + {{deployKey.title}} + +
      + {{deployKey.fingerprint}} +
      +
      + +
      + + created {{deployKey.created_at}} + +
      + Enable + + Remove + Disable +
      +
    • ` + } + + deployKeyRowComponent() { + const self = this; + return { + props: { + deployKey: Object, + enabled: Boolean + }, + + computed: { + disableURL() { + return self.disableEndpoint.replace(':id', this.deployKey.id); + }, + + enableURL() { + return self.enableEndpoint.replace(':id', this.deployKey.id); + } + }, + + template: this.deployKeyRowTemplate() + } + } + + initVue() { + const self = this; + const el = document.getElementById('js-deploy-keys'); + const endpoint = el.dataset.endpoint; + this.jsonEndpoint = `${endpoint}.json`; + this.disableEndpoint = `${endpoint}/:id/disable`; + this.enableEndpoint = `${endpoint}/:id/enable`; + new Vue({ + el: el, + components: { + deployKeyRow: self.deployKeyRowComponent() + }, + data () { + return { + enabledKeys: [], + availableKeys: [] + } + }, + created () { + this.$http.get(self.jsonEndpoint) + .then((res) => { + const keys = JSON.parse(res.body); + this.enabledKeys = keys.enabled_keys; + this.availableKeys = keys.available_project_keys; + }); + } + }) + } +} \ No newline at end of file diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml index 4cfbd9add00..b35cd356aa5 100644 --- a/app/views/projects/deploy_keys/_index.html.haml +++ b/app/views/projects/deploy_keys/_index.html.haml @@ -10,25 +10,16 @@ = render @deploy_keys.form_partial_path .col-lg-9.col-lg-offset-3 %hr - .col-lg-9.col-lg-offset-3.append-bottom-default.deploy-keys + #js-deploy-keys.col-lg-9.col-lg-offset-3.append-bottom-default{data:{endpoint: namespace_project_deploy_keys_path}} %h5.prepend-top-0 - Enabled deploy keys for this project (#{@deploy_keys.enabled_keys_size}) - - if @deploy_keys.any_keys_enabled? - %ul.well-list - = render partial: 'projects/deploy_keys/deploy_key', collection: @deploy_keys.enabled_keys, as: :deploy_key - - else - .settings-message.text-center - No deploy keys found. Create one with the form above. - %h5.prepend-top-default - Deploy keys from projects you have access to (#{@deploy_keys.available_project_keys_size}) - - if @deploy_keys.any_available_project_keys_enabled? - %ul.well-list - = render partial: 'projects/deploy_keys/deploy_key', collection: @deploy_keys.available_project_keys, as: :deploy_key - - else - .settings-message.text-center - No deploy keys from your projects could be found. Create one with the form above or add existing one below. - - if @deploy_keys.any_available_public_keys_enabled? - %h5.prepend-top-default - Public deploy keys available to any project (#{@deploy_keys.available_public_keys_size}) - %ul.well-list - = render partial: 'projects/deploy_keys/deploy_key', collection: @deploy_keys.available_public_keys, as: :deploy_key + Enabled deploy keys for this project ({{enabledKeys.length}}) + %ul.well-list{"v-if" => "enabledKeys.length"} + %deploy-key-row{"v-for" => "deployKey in enabledKeys", ":deploy-key" => "deployKey", ":enabled" =>"true"} + .settings-message.text-center{"v-else" => true} + No deploy keys found. Create one with the form above. + %h5.prepend-top-0 + Deploy keys from projects you have access to ({{availableKeys.length}}) + %ul.well-list{"v-if" => "availableKeys.length"} + %deploy-key-row{"v-for" => "deployKey in availableKeys", ":deploy-key" => "deployKey", ":enabled" =>"false"} + .settings-message.text-center{"v-else" => true} + No deploy keys found. Create one with the form above. -- cgit v1.2.1 From b5b33200ccef223e089edf6ad8cdd0ecc3e737da Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Thu, 27 Apr 2017 23:05:22 -0400 Subject: Add timeago --- app/assets/javascripts/settings/settings_repository.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/settings/settings_repository.js b/app/assets/javascripts/settings/settings_repository.js index 4c6f769f533..9d6a60baa3a 100644 --- a/app/assets/javascripts/settings/settings_repository.js +++ b/app/assets/javascripts/settings/settings_repository.js @@ -28,12 +28,12 @@ export default class SettingsDeployKeys {
      - created {{deployKey.created_at}} + created {{timeagoDate}}
      Enable - Remove + Remove Disable
      ` @@ -48,6 +48,10 @@ export default class SettingsDeployKeys { }, computed: { + timeagoDate() { + return gl.utils.getTimeago().format(this.deployKey.createdAt, 'gl_en'); + }, + disableURL() { return self.disableEndpoint.replace(':id', this.deployKey.id); }, -- cgit v1.2.1 From 471888d60e0d91f4ab75e5d773bd69dee85e6cea Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Apr 2017 11:08:18 +0100 Subject: Moved sort endpoints into data attributes --- app/assets/javascripts/milestone.js | 27 +++++++++++++++++---------- app/views/shared/milestones/_tabs.html.haml | 6 +++--- spec/support/milestone_tabs_examples.rb | 1 + 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index cd2bd976160..841b24a60a3 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -19,12 +19,10 @@ }); }; - Milestone.sortIssues = function(data) { - var sort_issues_url; - sort_issues_url = location.pathname + "/sort_issues"; + Milestone.sortIssues = function(url, data) { return $.ajax({ type: "PUT", - url: sort_issues_url, + url, data: data, success: function(_data) { return Milestone.successCallback(_data); @@ -36,12 +34,10 @@ }); }; - Milestone.sortMergeRequests = function(data) { - var sort_mr_url; - sort_mr_url = location.pathname + "/sort_merge_requests"; + Milestone.sortMergeRequests = function(url, data) { return $.ajax({ type: "PUT", - url: sort_mr_url, + url, data: data, success: function(_data) { return Milestone.successCallback(_data); @@ -81,6 +77,9 @@ }; function Milestone() { + this.issuesSortEndpoint = $('#tab-issues').data('sort-endpoint'); + this.mergeRequestsSortEndpoint = $('#tab-merge-requests').data('sort-endpoint'); + this.bindIssuesSorting(); this.bindTabsSwitching(); @@ -92,12 +91,16 @@ } Milestone.prototype.bindIssuesSorting = function() { + if (!this.issuesSortEndpoint) return; + $('#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed').each(function (i, el) { this.createSortable(el, { group: 'issue-list', listEls: $('.issues-sortable-list'), fieldName: 'issue', - sortCallback: Milestone.sortIssues, + sortCallback: (data) => { + Milestone.sortIssues(this.issuesSortEndpoint, data); + }, updateCallback: Milestone.updateIssue, }); }.bind(this)); @@ -113,12 +116,16 @@ }; Milestone.prototype.bindMergeRequestSorting = function() { + if (!this.mergeRequestsSortEndpoint) return; + $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").each(function (i, el) { this.createSortable(el, { group: 'merge-request-list', listEls: $(".merge_requests-sortable-list:not(#merge_requests-list-merged)"), fieldName: 'merge_request', - sortCallback: Milestone.sortMergeRequests, + sortCallback: (data) => { + Milestone.sortMergeRequests(this.mergeRequestsSortEndpoint, data); + }, updateCallback: Milestone.updateMergeRequest, }); }.bind(this)); diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml index 6717d59f033..6a6d817b344 100644 --- a/app/views/shared/milestones/_tabs.html.haml +++ b/app/views/shared/milestones/_tabs.html.haml @@ -30,13 +30,13 @@ .tab-content.milestone-content - if milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project) - .tab-pane.active#tab-issues + .tab-pane.active#tab-issues{ data: { sort_endpoint: (sort_issues_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } } = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user).include_associations, show_project_name: show_project_name, show_full_project_name: show_full_project_name - .tab-pane#tab-merge-requests + .tab-pane#tab-merge-requests{ data: { sort_endpoint: (sort_merge_requests_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } } -# loaded async = render "shared/milestones/tab_loading" - else - .tab-pane.active#tab-merge-requests + .tab-pane.active#tab-merge-requests{ data: { sort_endpoint: (sort_merge_requests_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } } -# loaded async = render "shared/milestones/tab_loading" .tab-pane#tab-participants diff --git a/spec/support/milestone_tabs_examples.rb b/spec/support/milestone_tabs_examples.rb index c48ecb1672e..c69f8e11008 100644 --- a/spec/support/milestone_tabs_examples.rb +++ b/spec/support/milestone_tabs_examples.rb @@ -8,6 +8,7 @@ shared_examples 'milestone tabs' do get path, params.merge(extra_params) end + describe '#merge_requests' do context 'as html' do before { go(:merge_requests, format: 'html') } -- cgit v1.2.1 From 6f89bd3628322d0c17f97163d060e6ef7238de3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20=22BKC=22=20Carlb=C3=A4cker?= Date: Wed, 19 Apr 2017 11:06:20 +0200 Subject: Use Gitaly for getting Branch/Tag counts - Backup-rake-spec fixed. Storage config broken - Use rugged to compare branch/tag-count in specs - upgrade gitaly --- GITALY_SERVER_VERSION | 2 +- app/models/repository.rb | 8 ++------ lib/gitlab/git/repository.rb | 29 +++++++++++++++++++++++------ lib/gitlab/gitaly_client/ref.rb | 8 ++++++++ spec/models/repository_spec.rb | 5 +++++ spec/tasks/gitlab/backup_rake_spec.rb | 6 ++++-- 6 files changed, 43 insertions(+), 15 deletions(-) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index a918a2aa18d..a3df0a6959e 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.6.0 +0.8.0 diff --git a/app/models/repository.rb b/app/models/repository.rb index d02aea49689..ce1238e66d2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -505,14 +505,10 @@ class Repository delegate :tag_names, to: :raw_repository cache_method :tag_names, fallback: [] - def branch_count - branches.size - end + delegate :branch_count, to: :raw_repository cache_method :branch_count, fallback: 0 - def tag_count - raw_repository.rugged.tags.count - end + delegate :tag_count, to: :raw_repository cache_method :tag_count, fallback: 0 def avatar diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 452dba7971d..4af7f1396c7 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -122,13 +122,30 @@ module Gitlab # Returns the number of valid branches def branch_count - rugged.branches.count do |ref| - begin - ref.name && ref.target # ensures the branch is valid + Gitlab::GitalyClient.migrate(:branch_names) do |is_enabled| + if is_enabled + gitaly_ref_client.count_branch_names + else + rugged.branches.count do |ref| + begin + ref.name && ref.target # ensures the branch is valid - true - rescue Rugged::ReferenceError - false + true + rescue Rugged::ReferenceError + false + end + end + end + end + end + + # Returns the number of valid tags + def tag_count + Gitlab::GitalyClient.migrate(:tag_names) do |is_enabled| + if is_enabled + gitaly_ref_client.count_tag_names + else + rugged.tags.count end end end diff --git a/lib/gitlab/gitaly_client/ref.rb b/lib/gitlab/gitaly_client/ref.rb index d3c0743db4e..2a5e8f73e55 100644 --- a/lib/gitlab/gitaly_client/ref.rb +++ b/lib/gitlab/gitaly_client/ref.rb @@ -34,6 +34,14 @@ module Gitlab stub.find_ref_name(request).name end + def count_tag_names + tag_names.count + end + + def count_branch_names + branch_names.count + end + private def consume_refs_response(response, prefix:) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 98d0641443e..4526c3194b6 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1379,12 +1379,17 @@ describe Repository, models: true do describe '#branch_count' do it 'returns the number of branches' do expect(repository.branch_count).to be_an(Integer) + rugged_count = repository.raw_repository.rugged.branches.count + expect(repository.branch_count).to eq(rugged_count) end end describe '#tag_count' do it 'returns the number of tags' do expect(repository.tag_count).to be_an(Integer) + # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync + rugged_count = repository.raw_repository.rugged.tags.count + expect(repository.tag_count).to eq(rugged_count) end end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 0a4a6ed8145..df2f2ce95e6 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -230,11 +230,13 @@ describe 'gitlab:app namespace rake task' do before do FileUtils.mkdir('tmp/tests/default_storage') FileUtils.mkdir('tmp/tests/custom_storage') + gitaly_address = Gitlab.config.repositories.storages.default.gitaly_address storages = { - 'default' => { 'path' => Settings.absolute('tmp/tests/default_storage') }, - 'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage') } + 'default' => { 'path' => Settings.absolute('tmp/tests/default_storage'), 'gitaly_address' => gitaly_address }, + 'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage'), 'gitaly_address' => gitaly_address } } allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) + Gitlab::GitalyClient.configure_channels # Create the projects now, after mocking the settings but before doing the backup project_a -- cgit v1.2.1 From a998710a3b2d4d2f5fa7dbdaf4ae468cd304730c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20=22BKC=22=20Carlb=C3=A4cker?= Date: Wed, 26 Apr 2017 18:33:48 +0200 Subject: Fix RSpec --- spec/lib/gitlab/git/repository_spec.rb | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index f88653cb1fe..1b78910fa3c 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1074,20 +1074,8 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#branch_count' do - before(:each) do - valid_ref = double(:ref) - invalid_ref = double(:ref) - - allow(valid_ref).to receive_messages(name: 'master', target: double(:target)) - - allow(invalid_ref).to receive_messages(name: 'bad-branch') - allow(invalid_ref).to receive(:target) { raise Rugged::ReferenceError } - - allow(repository.rugged).to receive_messages(branches: [valid_ref, invalid_ref]) - end - it 'returns the number of branches' do - expect(repository.branch_count).to eq(1) + expect(repository.branch_count).to eq(9) end end -- cgit v1.2.1 From 7fd3a5883e5a6135873f2e6def9dabf2ab82f361 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Apr 2017 12:11:30 +0100 Subject: Changed spec to a view spec --- spec/features/projects/commit/container_spec.rb | 23 ------------ spec/views/projects/commit/show.html.haml_spec.rb | 45 +++++++++++++++++++++++ 2 files changed, 45 insertions(+), 23 deletions(-) delete mode 100644 spec/features/projects/commit/container_spec.rb create mode 100644 spec/views/projects/commit/show.html.haml_spec.rb diff --git a/spec/features/projects/commit/container_spec.rb b/spec/features/projects/commit/container_spec.rb deleted file mode 100644 index 80b758ec14f..00000000000 --- a/spec/features/projects/commit/container_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'spec_helper' - -describe 'Commit container', :js, :feature do - let(:user) { create(:user) } - let(:project) { create(:project) } - - before do - project.team << [user, :master] - login_as(user) - end - - it 'keeps container-limited when view type is inline' do - visit namespace_project_commit_path(project.namespace, project, project.commit.id, view: :inline) - - expect(page).not_to have_selector('.limit-container-width') - end - - it 'diff spans full width when view type is parallel' do - visit namespace_project_commit_path(project.namespace, project, project.commit.id, view: :parallel) - - expect(page).to have_selector('.limit-container-width') - end -end diff --git a/spec/views/projects/commit/show.html.haml_spec.rb b/spec/views/projects/commit/show.html.haml_spec.rb new file mode 100644 index 00000000000..abbdc65ea71 --- /dev/null +++ b/spec/views/projects/commit/show.html.haml_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe 'projects/commit/show.html.haml', :view do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + + before do + assign(:project, project) + assign(:repository, project.repository) + assign(:commit, project.commit) + assign(:noteable, project.commit) + assign(:notes, []) + assign(:diffs, project.commit.diffs) + + allow(view).to receive(:current_user).and_return(user) + allow(view).to receive(:can?).and_return(false) + allow(view).to receive(:can_collaborate_with_project?).and_return(false) + allow(view).to receive(:current_ref).and_return(project.repository.root_ref) + allow(view).to receive(:diff_btn).and_return('') + end + + context 'inline diff view' do + before do + allow(view).to receive(:diff_view).and_return(:inline) + + render + end + + it 'keeps container-limited' do + expect(rendered).not_to have_selector('.limit-container-width') + end + end + + context 'parallel diff view' do + before do + allow(view).to receive(:diff_view).and_return(:parallel) + + render + end + + it 'spans full width' do + expect(rendered).to have_selector('.limit-container-width') + end + end +end -- cgit v1.2.1 From c504b88f07f03d381e7b2e22a129413319508413 Mon Sep 17 00:00:00 2001 From: Alexander Randa Date: Thu, 20 Apr 2017 08:31:37 +0000 Subject: Implement ability to update hooks --- app/controllers/admin/hooks_controller.rb | 26 ++- app/controllers/projects/hooks_controller.rb | 13 ++ app/views/admin/hooks/_form.html.haml | 40 +++++ app/views/admin/hooks/edit.html.haml | 14 ++ app/views/admin/hooks/index.html.haml | 55 +------ app/views/projects/hooks/_index.html.haml | 24 ++- app/views/projects/hooks/edit.html.haml | 14 ++ app/views/projects/settings/_head.html.haml | 2 +- .../settings/integrations/_project_hook.html.haml | 1 + app/views/shared/web_hooks/_form.html.haml | 182 +++++++++------------ changelogs/unreleased/19364-webhook-edit.yml | 4 + config/routes/admin.rb | 6 +- config/routes/project.rb | 2 +- spec/factories/project_hooks.rb | 2 + spec/features/admin/admin_hooks_spec.rb | 43 ++++- .../projects/settings/integration_settings_spec.rb | 94 +++++++++++ spec/routing/admin_routing_spec.rb | 14 +- spec/routing/project_routing_spec.rb | 6 +- 18 files changed, 372 insertions(+), 170 deletions(-) create mode 100644 app/views/admin/hooks/_form.html.haml create mode 100644 app/views/admin/hooks/edit.html.haml create mode 100644 app/views/projects/hooks/edit.html.haml create mode 100644 changelogs/unreleased/19364-webhook-edit.yml create mode 100644 spec/features/projects/settings/integration_settings_spec.rb diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index cbfc4581411..a119934febc 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -1,4 +1,6 @@ class Admin::HooksController < Admin::ApplicationController + before_action :hook, only: :edit + def index @hooks = SystemHook.all @hook = SystemHook.new @@ -15,15 +17,25 @@ class Admin::HooksController < Admin::ApplicationController end end + def edit + end + + def update + if hook.update_attributes(hook_params) + flash[:notice] = 'System hook was successfully updated.' + redirect_to admin_hooks_path + else + render 'edit' + end + end + def destroy - @hook = SystemHook.find(params[:id]) - @hook.destroy + hook.destroy redirect_to admin_hooks_path end def test - @hook = SystemHook.find(params[:hook_id]) data = { event_name: "project_create", name: "Ruby", @@ -32,11 +44,17 @@ class Admin::HooksController < Admin::ApplicationController owner_name: "Someone", owner_email: "example@gitlabhq.com" } - @hook.execute(data, 'system_hooks') + hook.execute(data, 'system_hooks') redirect_back_or_default end + private + + def hook + @hook ||= SystemHook.find(params[:id]) + end + def hook_params params.require(:hook).permit( :enable_ssl_verification, diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 1e41f980f31..86d13a0d222 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -1,6 +1,7 @@ class Projects::HooksController < Projects::ApplicationController # Authorize before_action :authorize_admin_project! + before_action :hook, only: :edit respond_to :html @@ -17,6 +18,18 @@ class Projects::HooksController < Projects::ApplicationController redirect_to namespace_project_settings_integrations_path(@project.namespace, @project) end + def edit + end + + def update + if hook.update_attributes(hook_params) + flash[:notice] = 'Hook was successfully updated.' + redirect_to namespace_project_settings_integrations_path(@project.namespace, @project) + else + render 'edit' + end + end + def test if !@project.empty_repo? status, message = TestHookService.new.execute(hook, current_user) diff --git a/app/views/admin/hooks/_form.html.haml b/app/views/admin/hooks/_form.html.haml new file mode 100644 index 00000000000..6217d5fb135 --- /dev/null +++ b/app/views/admin/hooks/_form.html.haml @@ -0,0 +1,40 @@ += form_errors(hook) + +.form-group + = form.label :url, 'URL', class: 'control-label' + .col-sm-10 + = form.text_field :url, class: 'form-control' +.form-group + = form.label :token, 'Secret Token', class: 'control-label' + .col-sm-10 + = form.text_field :token, class: 'form-control' + %p.help-block + Use this token to validate received payloads +.form-group + = form.label :url, 'Trigger', class: 'control-label' + .col-sm-10.prepend-top-10 + %div + System hook will be triggered on set of events like creating project + or adding ssh key. But you can also enable extra triggers like Push events. + + .prepend-top-default + = form.check_box :push_events, class: 'pull-left' + .prepend-left-20 + = form.label :push_events, class: 'list-label' do + %strong Push events + %p.light + This url will be triggered by a push to the repository + %div + = form.check_box :tag_push_events, class: 'pull-left' + .prepend-left-20 + = form.label :tag_push_events, class: 'list-label' do + %strong Tag push events + %p.light + This url will be triggered when a new tag is pushed to the repository +.form-group + = form.label :enable_ssl_verification, 'SSL verification', class: 'control-label checkbox' + .col-sm-10 + .checkbox + = form.label :enable_ssl_verification do + = form.check_box :enable_ssl_verification + %strong Enable SSL verification diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml new file mode 100644 index 00000000000..0777f5e2629 --- /dev/null +++ b/app/views/admin/hooks/edit.html.haml @@ -0,0 +1,14 @@ +- page_title 'Edit System Hook' +%h3.page-title + Edit System Hook + +%p.light + #{link_to 'System hooks ', help_page_path('system_hooks/system_hooks'), class: 'vlink'} can be + used for binding events when GitLab creates a User or Project. + +%hr + += form_for @hook, as: :hook, url: admin_hook_path, html: { class: 'form-horizontal' } do |f| + = render partial: 'form', locals: { form: f, hook: @hook } + .form-actions + = f.submit 'Save changes', class: 'btn btn-create' diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml index d9c7948763a..71117758921 100644 --- a/app/views/admin/hooks/index.html.haml +++ b/app/views/admin/hooks/index.html.haml @@ -1,57 +1,17 @@ -- page_title "System Hooks" +- page_title 'System Hooks' %h3.page-title System hooks %p.light - #{link_to "System hooks ", help_page_path("system_hooks/system_hooks"), class: "vlink"} can be + #{link_to 'System hooks ', help_page_path('system_hooks/system_hooks'), class: 'vlink'} can be used for binding events when GitLab creates a User or Project. %hr - = form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-horizontal' } do |f| - = form_errors(@hook) - - .form-group - = f.label :url, 'URL', class: 'control-label' - .col-sm-10 - = f.text_field :url, class: 'form-control' - .form-group - = f.label :token, 'Secret Token', class: 'control-label' - .col-sm-10 - = f.text_field :token, class: 'form-control' - %p.help-block - Use this token to validate received payloads - .form-group - = f.label :url, "Trigger", class: 'control-label' - .col-sm-10.prepend-top-10 - %div - System hook will be triggered on set of events like creating project - or adding ssh key. But you can also enable extra triggers like Push events. - - .prepend-top-default - = f.check_box :push_events, class: 'pull-left' - .prepend-left-20 - = f.label :push_events, class: 'list-label' do - %strong Push events - %p.light - This url will be triggered by a push to the repository - %div - = f.check_box :tag_push_events, class: 'pull-left' - .prepend-left-20 - = f.label :tag_push_events, class: 'list-label' do - %strong Tag push events - %p.light - This url will be triggered when a new tag is pushed to the repository - .form-group - = f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox' - .col-sm-10 - .checkbox - = f.label :enable_ssl_verification do - = f.check_box :enable_ssl_verification - %strong Enable SSL verification + = render partial: 'form', locals: { form: f, hook: @hook } .form-actions - = f.submit "Add system hook", class: "btn btn-create" + = f.submit 'Add system hook', class: 'btn btn-create' %hr - if @hooks.any? @@ -62,11 +22,12 @@ - @hooks.each do |hook| %li .controls - = link_to 'Test hook', admin_hook_test_path(hook), class: "btn btn-sm" - = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm" + = link_to 'Test hook', test_admin_hook_path(hook), class: 'btn btn-sm' + = link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm' + = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm' .monospace= hook.url %div - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger| - if hook.send(trigger) %span.label.label-gray= trigger.titleize - %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} + %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'} diff --git a/app/views/projects/hooks/_index.html.haml b/app/views/projects/hooks/_index.html.haml index 8faad351463..676b7c345bc 100644 --- a/app/views/projects/hooks/_index.html.haml +++ b/app/views/projects/hooks/_index.html.haml @@ -1 +1,23 @@ -= render 'shared/web_hooks/form', hook: @hook, hooks: @hooks, url_components: [@project.namespace.becomes(Namespace), @project] +.row.prepend-top-default + .col-lg-3 + %h4.prepend-top-0 + = page_title + %p + #{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be + used for binding events when something is happening within the project. + + .col-lg-9.append-bottom-default + = form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f| + = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } + = f.submit 'Add webhook', class: 'btn btn-create' + + %hr + %h5.prepend-top-default + Webhooks (#{@hooks.count}) + - if @hooks.any? + %ul.well-list + - @hooks.each do |hook| + = render 'project_hook', hook: hook + - else + %p.settings-message.text-center.append-bottom-0 + No webhooks found, add one in the form above. diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml new file mode 100644 index 00000000000..7998713be1f --- /dev/null +++ b/app/views/projects/hooks/edit.html.haml @@ -0,0 +1,14 @@ += render 'projects/settings/head' + +.row.prepend-top-default + .col-lg-3 + %h4.prepend-top-0 + = page_title + %p + #{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be + used for binding events when something is happening within the project. + .col-lg-9.append-bottom-default + = form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hook_path do |f| + = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook } + = f.submit 'Save changes', class: 'btn btn-create' + diff --git a/app/views/projects/settings/_head.html.haml b/app/views/projects/settings/_head.html.haml index e50a543ffa8..5a5ade03624 100644 --- a/app/views/projects/settings/_head.html.haml +++ b/app/views/projects/settings/_head.html.haml @@ -14,7 +14,7 @@ %span Members - if can_edit - = nav_link(controller: [:integrations, :services]) do + = nav_link(controller: [:integrations, :services, :hooks]) do = link_to project_settings_integrations_path(@project), title: 'Integrations' do %span Integrations diff --git a/app/views/projects/settings/integrations/_project_hook.html.haml b/app/views/projects/settings/integrations/_project_hook.html.haml index ceabe2eab3d..8dc276a3bec 100644 --- a/app/views/projects/settings/integrations/_project_hook.html.haml +++ b/app/views/projects/settings/integrations/_project_hook.html.haml @@ -9,6 +9,7 @@ .col-md-4.col-lg-5.text-right-lg.prepend-top-5 %span.append-right-10.inline SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} + = link_to "Edit", edit_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm" = link_to "Test", test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm" = link_to namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do %span.sr-only Remove diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml index ee3be3c789a..37c3e61912c 100644 --- a/app/views/shared/web_hooks/_form.html.haml +++ b/app/views/shared/web_hooks/_form.html.haml @@ -1,102 +1,82 @@ -.row.prepend-top-default - .col-lg-3 - %h4.prepend-top-0 - = page_title - %p - #{link_to "Webhooks", help_page_path("user/project/integrations/webhooks")} can be - used for binding events when something is happening within the project. - .col-lg-9.append-bottom-default - = form_for hook, as: :hook, url: polymorphic_path(url_components + [:hooks]) do |f| - = form_errors(hook) += form_errors(hook) - .form-group - = f.label :url, "URL", class: 'label-light' - = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json' - .form-group - = f.label :token, "Secret Token", class: 'label-light' - = f.text_field :token, class: "form-control", placeholder: '' - %p.help-block - Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header. - .form-group - = f.label :url, "Trigger", class: 'label-light' - %ul.list-unstyled - %li - = f.check_box :push_events, class: 'pull-left' - .prepend-left-20 - = f.label :push_events, class: 'list-label' do - %strong Push events - %p.light - This URL will be triggered by a push to the repository - %li - = f.check_box :tag_push_events, class: 'pull-left' - .prepend-left-20 - = f.label :tag_push_events, class: 'list-label' do - %strong Tag push events - %p.light - This URL will be triggered when a new tag is pushed to the repository - %li - = f.check_box :note_events, class: 'pull-left' - .prepend-left-20 - = f.label :note_events, class: 'list-label' do - %strong Comments - %p.light - This URL will be triggered when someone adds a comment - %li - = f.check_box :issues_events, class: 'pull-left' - .prepend-left-20 - = f.label :issues_events, class: 'list-label' do - %strong Issues events - %p.light - This URL will be triggered when an issue is created/updated/merged - %li - = f.check_box :confidential_issues_events, class: 'pull-left' - .prepend-left-20 - = f.label :confidential_issues_events, class: 'list-label' do - %strong Confidential Issues events - %p.light - This URL will be triggered when a confidential issue is created/updated/merged - %li - = f.check_box :merge_requests_events, class: 'pull-left' - .prepend-left-20 - = f.label :merge_requests_events, class: 'list-label' do - %strong Merge Request events - %p.light - This URL will be triggered when a merge request is created/updated/merged - %li - = f.check_box :build_events, class: 'pull-left' - .prepend-left-20 - = f.label :build_events, class: 'list-label' do - %strong Jobs events - %p.light - This URL will be triggered when the job status changes - %li - = f.check_box :pipeline_events, class: 'pull-left' - .prepend-left-20 - = f.label :pipeline_events, class: 'list-label' do - %strong Pipeline events - %p.light - This URL will be triggered when the pipeline status changes - %li - = f.check_box :wiki_page_events, class: 'pull-left' - .prepend-left-20 - = f.label :wiki_page_events, class: 'list-label' do - %strong Wiki Page events - %p.light - This URL will be triggered when a wiki page is created/updated - .form-group - = f.label :enable_ssl_verification, "SSL verification", class: 'label-light checkbox' - .checkbox - = f.label :enable_ssl_verification do - = f.check_box :enable_ssl_verification - %strong Enable SSL verification - = f.submit "Add webhook", class: "btn btn-create" - %hr - %h5.prepend-top-default - Webhooks (#{hooks.count}) - - if hooks.any? - %ul.well-list - - hooks.each do |hook| - = render "project_hook", hook: hook - - else - %p.settings-message.text-center.append-bottom-0 - No webhooks found, add one in the form above. +.form-group + = form.label :url, 'URL', class: 'label-light' + = form.text_field :url, class: 'form-control', placeholder: 'http://example.com/trigger-ci.json' +.form-group + = form.label :token, 'Secret Token', class: 'label-light' + = form.text_field :token, class: 'form-control', placeholder: '' + %p.help-block + Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header. +.form-group + = form.label :url, 'Trigger', class: 'label-light' + %ul.list-unstyled + %li + = form.check_box :push_events, class: 'pull-left' + .prepend-left-20 + = form.label :push_events, class: 'list-label' do + %strong Push events + %p.light + This URL will be triggered by a push to the repository + %li + = form.check_box :tag_push_events, class: 'pull-left' + .prepend-left-20 + = form.label :tag_push_events, class: 'list-label' do + %strong Tag push events + %p.light + This URL will be triggered when a new tag is pushed to the repository + %li + = form.check_box :note_events, class: 'pull-left' + .prepend-left-20 + = form.label :note_events, class: 'list-label' do + %strong Comments + %p.light + This URL will be triggered when someone adds a comment + %li + = form.check_box :issues_events, class: 'pull-left' + .prepend-left-20 + = form.label :issues_events, class: 'list-label' do + %strong Issues events + %p.light + This URL will be triggered when an issue is created/updated/merged + %li + = form.check_box :confidential_issues_events, class: 'pull-left' + .prepend-left-20 + = form.label :confidential_issues_events, class: 'list-label' do + %strong Confidential Issues events + %p.light + This URL will be triggered when a confidential issue is created/updated/merged + %li + = form.check_box :merge_requests_events, class: 'pull-left' + .prepend-left-20 + = form.label :merge_requests_events, class: 'list-label' do + %strong Merge Request events + %p.light + This URL will be triggered when a merge request is created/updated/merged + %li + = form.check_box :build_events, class: 'pull-left' + .prepend-left-20 + = form.label :build_events, class: 'list-label' do + %strong Jobs events + %p.light + This URL will be triggered when the job status changes + %li + = form.check_box :pipeline_events, class: 'pull-left' + .prepend-left-20 + = form.label :pipeline_events, class: 'list-label' do + %strong Pipeline events + %p.light + This URL will be triggered when the pipeline status changes + %li + = form.check_box :wiki_page_events, class: 'pull-left' + .prepend-left-20 + = form.label :wiki_page_events, class: 'list-label' do + %strong Wiki Page events + %p.light + This URL will be triggered when a wiki page is created/updated +.form-group + = form.label :enable_ssl_verification, 'SSL verification', class: 'label-light checkbox' + .checkbox + = form.label :enable_ssl_verification do + = form.check_box :enable_ssl_verification + %strong Enable SSL verification diff --git a/changelogs/unreleased/19364-webhook-edit.yml b/changelogs/unreleased/19364-webhook-edit.yml new file mode 100644 index 00000000000..60e154b8b83 --- /dev/null +++ b/changelogs/unreleased/19364-webhook-edit.yml @@ -0,0 +1,4 @@ +--- +title: Implement ability to edit hooks +merge_request: 10816 +author: Alexander Randa diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 52ba10604d4..48993420ed9 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -50,8 +50,10 @@ namespace :admin do resources :deploy_keys, only: [:index, :new, :create, :destroy] - resources :hooks, only: [:index, :create, :destroy] do - get :test + resources :hooks, only: [:index, :create, :edit, :update, :destroy] do + member do + get :test + end end resources :broadcast_messages, only: [:index, :edit, :create, :update, :destroy] do diff --git a/config/routes/project.rb b/config/routes/project.rb index 115ae2324b3..71fc84a7a12 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -185,7 +185,7 @@ constraints(ProjectUrlConstrainer.new) do end end - resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do + resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do member do get :test end diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb index 39c2a9dd1fb..0210e871a63 100644 --- a/spec/factories/project_hooks.rb +++ b/spec/factories/project_hooks.rb @@ -1,6 +1,7 @@ FactoryGirl.define do factory :project_hook do url { generate(:url) } + enable_ssl_verification false trait :token do token { SecureRandom.hex(10) } @@ -11,6 +12,7 @@ FactoryGirl.define do merge_requests_events true tag_push_events true issues_events true + confidential_issues_events true note_events true build_events true pipeline_events true diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index fb519a9bf12..c5f24d412d7 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Admin::Hooks", feature: true do +describe 'Admin::Hooks', feature: true do before do @project = create(:project) login_as :admin @@ -8,24 +8,24 @@ describe "Admin::Hooks", feature: true do @system_hook = create(:system_hook) end - describe "GET /admin/hooks" do - it "is ok" do + describe 'GET /admin/hooks' do + it 'is ok' do visit admin_root_path - page.within ".layout-nav" do - click_on "Hooks" + page.within '.layout-nav' do + click_on 'Hooks' end expect(current_path).to eq(admin_hooks_path) end - it "has hooks list" do + it 'has hooks list' do visit admin_hooks_path expect(page).to have_content(@system_hook.url) end end - describe "New Hook" do + describe 'New Hook' do let(:url) { generate(:url) } it 'adds new hook' do @@ -40,11 +40,36 @@ describe "Admin::Hooks", feature: true do end end - describe "Test" do + describe 'Update existing hook' do + let(:new_url) { generate(:url) } + + it 'updates existing hook' do + visit admin_hooks_path + + click_link 'Edit' + fill_in 'hook_url', with: new_url + check 'Enable SSL verification' + click_button 'Save changes' + + expect(page).to have_content 'SSL Verification: enabled' + expect(current_path).to eq(admin_hooks_path) + expect(page).to have_content(new_url) + end + end + + describe 'Remove existing hook' do + it 'remove existing hook' do + visit admin_hooks_path + + expect { click_link 'Remove' }.to change(SystemHook, :count).by(-1) + end + end + + describe 'Test' do before do WebMock.stub_request(:post, @system_hook.url) visit admin_hooks_path - click_link "Test hook" + click_link 'Test hook' end it { expect(current_path).to eq(admin_hooks_path) } diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb new file mode 100644 index 00000000000..7909234556e --- /dev/null +++ b/spec/features/projects/settings/integration_settings_spec.rb @@ -0,0 +1,94 @@ +require 'spec_helper' + +feature 'Integration settings', feature: true do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + let(:role) { :developer } + let(:integrations_path) { namespace_project_settings_integrations_path(project.namespace, project) } + + background do + login_as(user) + project.team << [user, role] + end + + context 'for developer' do + given(:role) { :developer } + + scenario 'to be disallowed to view' do + visit integrations_path + + expect(page.status_code).to eq(404) + end + end + + context 'for master' do + given(:role) { :master } + + context 'Webhooks' do + let(:hook) { create(:project_hook, :all_events_enabled, enable_ssl_verification: true, project: project) } + let(:url) { generate(:url) } + + scenario 'show list of webhooks' do + hook + + visit integrations_path + + expect(page.status_code).to eq(200) + expect(page).to have_content(hook.url) + expect(page).to have_content('SSL Verification: enabled') + expect(page).to have_content('Push Events') + expect(page).to have_content('Tag Push Events') + expect(page).to have_content('Issues Events') + expect(page).to have_content('Confidential Issues Events') + expect(page).to have_content('Note Events') + expect(page).to have_content('Merge Requests Events') + expect(page).to have_content('Pipeline Events') + expect(page).to have_content('Wiki Page Events') + end + + scenario 'create webhook' do + visit integrations_path + + fill_in 'hook_url', with: url + check 'Tag push events' + check 'Enable SSL verification' + + click_button 'Add webhook' + + expect(page).to have_content(url) + expect(page).to have_content('SSL Verification: enabled') + expect(page).to have_content('Push Events') + expect(page).to have_content('Tag Push Events') + end + + scenario 'edit existing webhook' do + hook + visit integrations_path + + click_link 'Edit' + fill_in 'hook_url', with: url + check 'Enable SSL verification' + click_button 'Save changes' + + expect(page).to have_content 'SSL Verification: enabled' + expect(page).to have_content(url) + end + + scenario 'test existing webhook' do + WebMock.stub_request(:post, hook.url) + visit integrations_path + + click_link 'Test' + + expect(current_path).to eq(integrations_path) + end + + scenario 'remove existing webhook' do + hook + visit integrations_path + + expect { click_link 'Remove' }.to change(ProjectHook, :count).by(-1) + end + end + end +end diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 99c44bde151..e5fc0b676af 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -71,13 +71,15 @@ describe Admin::ProjectsController, "routing" do end end -# admin_hook_test GET /admin/hooks/:hook_id/test(.:format) admin/hooks#test +# admin_hook_test GET /admin/hooks/:id/test(.:format) admin/hooks#test # admin_hooks GET /admin/hooks(.:format) admin/hooks#index # POST /admin/hooks(.:format) admin/hooks#create # admin_hook DELETE /admin/hooks/:id(.:format) admin/hooks#destroy +# PUT /admin/hooks/:id(.:format) admin/hooks#update +# edit_admin_hook GET /admin/hooks/:id(.:format) admin/hooks#edit describe Admin::HooksController, "routing" do it "to #test" do - expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', hook_id: '1') + expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1') end it "to #index" do @@ -88,6 +90,14 @@ describe Admin::HooksController, "routing" do expect(post("/admin/hooks")).to route_to('admin/hooks#create') end + it "to #edit" do + expect(get("/admin/hooks/1/edit")).to route_to('admin/hooks#edit', id: '1') + end + + it "to #update" do + expect(put("/admin/hooks/1")).to route_to('admin/hooks#update', id: '1') + end + it "to #destroy" do expect(delete("/admin/hooks/1")).to route_to('admin/hooks#destroy', id: '1') end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index a3de022d242..163df072cf6 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -340,14 +340,16 @@ describe 'project routing' do # test_project_hook GET /:project_id/hooks/:id/test(.:format) hooks#test # project_hooks GET /:project_id/hooks(.:format) hooks#index # POST /:project_id/hooks(.:format) hooks#create - # project_hook DELETE /:project_id/hooks/:id(.:format) hooks#destroy + # edit_project_hook GET /:project_id/hooks/:id/edit(.:format) hooks#edit + # project_hook PUT /:project_id/hooks/:id(.:format) hooks#update + # DELETE /:project_id/hooks/:id(.:format) hooks#destroy describe Projects::HooksController, 'routing' do it 'to #test' do expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it_behaves_like 'RESTful project resources' do - let(:actions) { [:index, :create, :destroy] } + let(:actions) { [:index, :create, :destroy, :edit, :update] } let(:controller) { 'hooks' } end end -- cgit v1.2.1 From 573d0989110ccb0630481f416d1a4f8fd05ee608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20=22BKC=22=20Carlb=C3=A4cker?= Date: Fri, 28 Apr 2017 14:12:29 +0200 Subject: Cleanup --- app/models/repository.rb | 4 +--- spec/models/repository_spec.rb | 5 +++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index ce1238e66d2..2ffd9115372 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -505,10 +505,8 @@ class Repository delegate :tag_names, to: :raw_repository cache_method :tag_names, fallback: [] - delegate :branch_count, to: :raw_repository + delegate :branch_count, :tag_count, to: :raw_repository cache_method :branch_count, fallback: 0 - - delegate :tag_count, to: :raw_repository cache_method :tag_count, fallback: 0 def avatar diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 4526c3194b6..dfa211ec6cd 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1379,7 +1379,10 @@ describe Repository, models: true do describe '#branch_count' do it 'returns the number of branches' do expect(repository.branch_count).to be_an(Integer) + + # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync rugged_count = repository.raw_repository.rugged.branches.count + expect(repository.branch_count).to eq(rugged_count) end end @@ -1387,8 +1390,10 @@ describe Repository, models: true do describe '#tag_count' do it 'returns the number of tags' do expect(repository.tag_count).to be_an(Integer) + # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync rugged_count = repository.raw_repository.rugged.tags.count + expect(repository.tag_count).to eq(rugged_count) end end -- cgit v1.2.1 From e11a702afceee7f5edeb8c93a4192fa05209b091 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Apr 2017 17:06:19 +0100 Subject: Re-wrote to match our docs - still not 100% sure but closer than it was --- .../deploy_keys/components/action_btn.vue | 53 +++++++++++ .../javascripts/deploy_keys/components/app.vue | 105 +++++++++++++++++++++ .../javascripts/deploy_keys/components/key.vue | 85 +++++++++++++++++ .../javascripts/deploy_keys/components/keys.vue | 52 ++++++++++ app/assets/javascripts/deploy_keys/eventhub.js | 3 + app/assets/javascripts/deploy_keys/index.js | 21 +++++ .../javascripts/deploy_keys/service/index.js | 34 +++++++ app/assets/javascripts/deploy_keys/store/index.js | 13 +++ app/assets/javascripts/dispatcher.js | 3 - .../javascripts/settings/settings_repository.js | 96 ------------------- app/controllers/projects/deploy_keys_controller.rb | 11 ++- app/serializers/project_entity.rb | 4 +- app/views/projects/deploy_keys/_index.html.haml | 14 +-- .../projects/settings/repository/show.html.haml | 4 + config/webpack.config.js | 1 + 15 files changed, 384 insertions(+), 115 deletions(-) create mode 100644 app/assets/javascripts/deploy_keys/components/action_btn.vue create mode 100644 app/assets/javascripts/deploy_keys/components/app.vue create mode 100644 app/assets/javascripts/deploy_keys/components/key.vue create mode 100644 app/assets/javascripts/deploy_keys/components/keys.vue create mode 100644 app/assets/javascripts/deploy_keys/eventhub.js create mode 100644 app/assets/javascripts/deploy_keys/index.js create mode 100644 app/assets/javascripts/deploy_keys/service/index.js create mode 100644 app/assets/javascripts/deploy_keys/store/index.js delete mode 100644 app/assets/javascripts/settings/settings_repository.js diff --git a/app/assets/javascripts/deploy_keys/components/action_btn.vue b/app/assets/javascripts/deploy_keys/components/action_btn.vue new file mode 100644 index 00000000000..7da2915a45e --- /dev/null +++ b/app/assets/javascripts/deploy_keys/components/action_btn.vue @@ -0,0 +1,53 @@ + + + diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue new file mode 100644 index 00000000000..ff54a0f241a --- /dev/null +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -0,0 +1,105 @@ + + + diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue new file mode 100644 index 00000000000..af842a3bb39 --- /dev/null +++ b/app/assets/javascripts/deploy_keys/components/key.vue @@ -0,0 +1,85 @@ + + + diff --git a/app/assets/javascripts/deploy_keys/components/keys.vue b/app/assets/javascripts/deploy_keys/components/keys.vue new file mode 100644 index 00000000000..2470831eddf --- /dev/null +++ b/app/assets/javascripts/deploy_keys/components/keys.vue @@ -0,0 +1,52 @@ + + + diff --git a/app/assets/javascripts/deploy_keys/eventhub.js b/app/assets/javascripts/deploy_keys/eventhub.js new file mode 100644 index 00000000000..0948c2e5352 --- /dev/null +++ b/app/assets/javascripts/deploy_keys/eventhub.js @@ -0,0 +1,3 @@ +import Vue from 'vue'; + +export default new Vue(); diff --git a/app/assets/javascripts/deploy_keys/index.js b/app/assets/javascripts/deploy_keys/index.js new file mode 100644 index 00000000000..a5f232f950a --- /dev/null +++ b/app/assets/javascripts/deploy_keys/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue'; +import deployKeysApp from './components/app.vue'; + +document.addEventListener('DOMContentLoaded', () => new Vue({ + el: document.getElementById('js-deploy-keys'), + data() { + return { + endpoint: this.$options.el.dataset.endpoint, + }; + }, + components: { + deployKeysApp, + }, + render(createElement) { + return createElement('deploy-keys-app', { + props: { + endpoint: this.endpoint, + }, + }); + }, +})); diff --git a/app/assets/javascripts/deploy_keys/service/index.js b/app/assets/javascripts/deploy_keys/service/index.js new file mode 100644 index 00000000000..fe6dbaa9498 --- /dev/null +++ b/app/assets/javascripts/deploy_keys/service/index.js @@ -0,0 +1,34 @@ +import Vue from 'vue'; +import VueResource from 'vue-resource'; + +Vue.use(VueResource); + +export default class DeployKeysService { + constructor(endpoint) { + this.endpoint = endpoint; + + this.resource = Vue.resource(`${this.endpoint}{/id}`, {}, { + enable: { + method: 'PUT', + url: `${this.endpoint}{/id}/enable`, + }, + disable: { + method: 'PUT', + url: `${this.endpoint}{/id}/disable`, + }, + }); + } + + getKeys() { + return this.resource.get() + .then(response => response.json()); + } + + enableKey(id) { + return this.resource.enable({ id }, {}); + } + + disableKey(id) { + return this.resource.disable({ id }, {}); + } +} diff --git a/app/assets/javascripts/deploy_keys/store/index.js b/app/assets/javascripts/deploy_keys/store/index.js new file mode 100644 index 00000000000..7177d9bed7f --- /dev/null +++ b/app/assets/javascripts/deploy_keys/store/index.js @@ -0,0 +1,13 @@ +export default class DeployKeysStore { + constructor() { + this.keys = {}; + } + + findEnabledKey(id) { + return this.keys.enabled_keys.find(key => key.id === id); + } + + removeKeyForType(deployKey, type) { + this.keys[type] = this.keys[type].filter(key => key.id !== deployKey.id); + } +} diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index f7402792c59..d3d75c4bf4a 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -48,7 +48,6 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion'; import UserCallout from './user_callout'; import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags'; import ShortcutsWiki from './shortcuts_wiki'; -import SettingsDeployKeys from './settings/settings_repository'; import BlobViewer from './blob/viewer/index'; const ShortcutsBlob = require('./shortcuts_blob'); @@ -346,8 +345,6 @@ const ShortcutsBlob = require('./shortcuts_blob'); // Initialize Protected Tag Settings new ProtectedTagCreate(); new ProtectedTagEditList(); - // Initialize deploy key ajax call - new SettingsDeployKeys(); break; case 'projects:ci_cd:show': new gl.ProjectVariables(); diff --git a/app/assets/javascripts/settings/settings_repository.js b/app/assets/javascripts/settings/settings_repository.js deleted file mode 100644 index 9d6a60baa3a..00000000000 --- a/app/assets/javascripts/settings/settings_repository.js +++ /dev/null @@ -1,96 +0,0 @@ -/* eslint-disable no-new */ -import Vue from 'vue'; -import VueResource from 'vue-resource'; - -Vue.use(VueResource); - -export default class SettingsDeployKeys { - constructor() { - this.initVue(); - } - - deployKeyRowTemplate() { - return ` -
    • - -
      - - {{deployKey.title}} - -
      - {{deployKey.fingerprint}} -
      -
      - -
      - - created {{timeagoDate}} - -
      - Enable - - Remove - Disable -
      -
    • ` - } - - deployKeyRowComponent() { - const self = this; - return { - props: { - deployKey: Object, - enabled: Boolean - }, - - computed: { - timeagoDate() { - return gl.utils.getTimeago().format(this.deployKey.createdAt, 'gl_en'); - }, - - disableURL() { - return self.disableEndpoint.replace(':id', this.deployKey.id); - }, - - enableURL() { - return self.enableEndpoint.replace(':id', this.deployKey.id); - } - }, - - template: this.deployKeyRowTemplate() - } - } - - initVue() { - const self = this; - const el = document.getElementById('js-deploy-keys'); - const endpoint = el.dataset.endpoint; - this.jsonEndpoint = `${endpoint}.json`; - this.disableEndpoint = `${endpoint}/:id/disable`; - this.enableEndpoint = `${endpoint}/:id/enable`; - new Vue({ - el: el, - components: { - deployKeyRow: self.deployKeyRowComponent() - }, - data () { - return { - enabledKeys: [], - availableKeys: [] - } - }, - created () { - this.$http.get(self.jsonEndpoint) - .then((res) => { - const keys = JSON.parse(res.body); - this.enabledKeys = keys.enabled_keys; - this.availableKeys = keys.available_project_keys; - }); - } - }) - } -} \ No newline at end of file diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index a47e15a192b..f27089b8590 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -32,7 +32,10 @@ class Projects::DeployKeysController < Projects::ApplicationController def enable Projects::EnableDeployKeyService.new(@project, current_user, params).execute - redirect_to_repository_settings(@project) + respond_to do |format| + format.html { redirect_to_repository_settings(@project) } + format.json { head :ok } + end end def disable @@ -40,7 +43,11 @@ class Projects::DeployKeysController < Projects::ApplicationController return render_404 unless deploy_key_project deploy_key_project.destroy! - redirect_to_repository_settings(@project) + + respond_to do |format| + format.html { redirect_to_repository_settings(@project) } + format.json { head :ok } + end end protected diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb index 6f8061f7530..a471a7e6a88 100644 --- a/app/serializers/project_entity.rb +++ b/app/serializers/project_entity.rb @@ -1,9 +1,11 @@ class ProjectEntity < Grape::Entity + include RequestAwareEntity + expose :id expose :name expose :full_path do |project| - project.full_path + namespace_project_path(project.namespace, project) end expose :full_name do |project| diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml index b35cd356aa5..74756b58439 100644 --- a/app/views/projects/deploy_keys/_index.html.haml +++ b/app/views/projects/deploy_keys/_index.html.haml @@ -10,16 +10,4 @@ = render @deploy_keys.form_partial_path .col-lg-9.col-lg-offset-3 %hr - #js-deploy-keys.col-lg-9.col-lg-offset-3.append-bottom-default{data:{endpoint: namespace_project_deploy_keys_path}} - %h5.prepend-top-0 - Enabled deploy keys for this project ({{enabledKeys.length}}) - %ul.well-list{"v-if" => "enabledKeys.length"} - %deploy-key-row{"v-for" => "deployKey in enabledKeys", ":deploy-key" => "deployKey", ":enabled" =>"true"} - .settings-message.text-center{"v-else" => true} - No deploy keys found. Create one with the form above. - %h5.prepend-top-0 - Deploy keys from projects you have access to ({{availableKeys.length}}) - %ul.well-list{"v-if" => "availableKeys.length"} - %deploy-key-row{"v-for" => "deployKey in availableKeys", ":deploy-key" => "deployKey", ":enabled" =>"false"} - .settings-message.text-center{"v-else" => true} - No deploy keys found. Create one with the form above. + #js-deploy-keys{ data: { endpoint: namespace_project_deploy_keys_path } } diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index 5402320cb66..4e59033c4a3 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -1,6 +1,10 @@ - page_title "Repository" = render "projects/settings/head" +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag('common_vue') + = page_specific_javascript_bundle_tag('deploy_keys') + = render @deploy_keys = render "projects/protected_branches/index" = render "projects/protected_tags/index" diff --git a/config/webpack.config.js b/config/webpack.config.js index cb0a57a3a41..1721d275685 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -26,6 +26,7 @@ var config = { common_d3: ['d3'], cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', commit_pipelines: './commit/pipelines/pipelines_bundle.js', + deploy_keys: './deploy_keys/index.js', diff_notes: './diff_notes/diff_notes_bundle.js', environments: './environments/environments_bundle.js', environments_folder: './environments/folder/environments_folder_bundle.js', -- cgit v1.2.1 From fde9732a27065467f4fab0ba0a6bd1cfed9b092b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Apr 2017 17:11:46 +0100 Subject: Fixed public keys always rendering even when empty --- app/assets/javascripts/deploy_keys/components/app.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index ff54a0f241a..e0443e2e0df 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -97,6 +97,7 @@ :keys="keys.available_project_keys" :store="store" /> -- cgit v1.2.1 From 0b7824d433ec5029da17114389f425f816b7be74 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Apr 2017 17:22:57 +0100 Subject: Updated some Vue specific JS --- .../deploy_keys/components/action_btn.vue | 3 +- .../javascripts/deploy_keys/components/app.vue | 10 ++--- .../javascripts/deploy_keys/components/key.vue | 4 +- .../javascripts/deploy_keys/components/keys.vue | 52 ---------------------- .../deploy_keys/components/keys_panel.vue | 52 ++++++++++++++++++++++ app/assets/javascripts/deploy_keys/store/index.js | 4 -- changelogs/unreleased/29667-deploy-keys.yml | 4 -- changelogs/unreleased/deploy-keys-load-async.yml | 4 ++ 8 files changed, 65 insertions(+), 68 deletions(-) delete mode 100644 app/assets/javascripts/deploy_keys/components/keys.vue create mode 100644 app/assets/javascripts/deploy_keys/components/keys_panel.vue delete mode 100644 changelogs/unreleased/29667-deploy-keys.yml create mode 100644 changelogs/unreleased/deploy-keys-load-async.yml diff --git a/app/assets/javascripts/deploy_keys/components/action_btn.vue b/app/assets/javascripts/deploy_keys/components/action_btn.vue index 7da2915a45e..3ff3a9d977e 100644 --- a/app/assets/javascripts/deploy_keys/components/action_btn.vue +++ b/app/assets/javascripts/deploy_keys/components/action_btn.vue @@ -47,7 +47,8 @@ diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index e0443e2e0df..ee2f85bde53 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -3,15 +3,13 @@ import eventHub from '../eventhub'; import DeployKeysService from '../service'; import DeployKeysStore from '../store'; - import keysPanel from './keys.vue'; + import keysPanel from './keys_panel.vue'; export default { data() { - const store = new DeployKeysStore(); - return { isLoading: false, - store, + store: new DeployKeysStore(), }; }, props: { @@ -84,7 +82,9 @@ class="text-center" v-if="isLoading && !hasKeys"> + class="fa fa-spinner fa-spin fa-2x" + aria-hidden="true" + aria-label="Loading deploy keys">
      diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue index af842a3bb39..1b47cceba78 100644 --- a/app/assets/javascripts/deploy_keys/components/key.vue +++ b/app/assets/javascripts/deploy_keys/components/key.vue @@ -56,9 +56,9 @@
      diff --git a/app/assets/javascripts/deploy_keys/components/keys.vue b/app/assets/javascripts/deploy_keys/components/keys.vue deleted file mode 100644 index 2470831eddf..00000000000 --- a/app/assets/javascripts/deploy_keys/components/keys.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - diff --git a/app/assets/javascripts/deploy_keys/components/keys_panel.vue b/app/assets/javascripts/deploy_keys/components/keys_panel.vue new file mode 100644 index 00000000000..2470831eddf --- /dev/null +++ b/app/assets/javascripts/deploy_keys/components/keys_panel.vue @@ -0,0 +1,52 @@ + + + diff --git a/app/assets/javascripts/deploy_keys/store/index.js b/app/assets/javascripts/deploy_keys/store/index.js index 7177d9bed7f..6210361af26 100644 --- a/app/assets/javascripts/deploy_keys/store/index.js +++ b/app/assets/javascripts/deploy_keys/store/index.js @@ -6,8 +6,4 @@ export default class DeployKeysStore { findEnabledKey(id) { return this.keys.enabled_keys.find(key => key.id === id); } - - removeKeyForType(deployKey, type) { - this.keys[type] = this.keys[type].filter(key => key.id !== deployKey.id); - } } diff --git a/changelogs/unreleased/29667-deploy-keys.yml b/changelogs/unreleased/29667-deploy-keys.yml deleted file mode 100644 index 0f202ebf1ee..00000000000 --- a/changelogs/unreleased/29667-deploy-keys.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Project deploy keys json end point -merge_request: -author: diff --git a/changelogs/unreleased/deploy-keys-load-async.yml b/changelogs/unreleased/deploy-keys-load-async.yml new file mode 100644 index 00000000000..e90910278e8 --- /dev/null +++ b/changelogs/unreleased/deploy-keys-load-async.yml @@ -0,0 +1,4 @@ +--- +title: Deploy keys load are loaded async +merge_request: +author: -- cgit v1.2.1 From cd9d1c6f042916d35e5b46be8e086437db3ec11f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 27 Apr 2017 18:16:45 +0200 Subject: Document pipeline grouping --- doc/ci/img/pipelines_grouped.png | Bin 0 -> 12937 bytes doc/ci/img/pipelines_index.png | Bin 0 -> 36299 bytes doc/ci/img/pipelines_mini_graph.png | Bin 0 -> 15404 bytes doc/ci/img/pipelines_mini_graph_simple.png | Bin 0 -> 1637 bytes doc/ci/pipelines.md | 126 +++++++++++++++++++++++++++-- 5 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 doc/ci/img/pipelines_grouped.png create mode 100644 doc/ci/img/pipelines_index.png create mode 100644 doc/ci/img/pipelines_mini_graph.png create mode 100644 doc/ci/img/pipelines_mini_graph_simple.png diff --git a/doc/ci/img/pipelines_grouped.png b/doc/ci/img/pipelines_grouped.png new file mode 100644 index 00000000000..06f52e03320 Binary files /dev/null and b/doc/ci/img/pipelines_grouped.png differ diff --git a/doc/ci/img/pipelines_index.png b/doc/ci/img/pipelines_index.png new file mode 100644 index 00000000000..3b522a9c5e4 Binary files /dev/null and b/doc/ci/img/pipelines_index.png differ diff --git a/doc/ci/img/pipelines_mini_graph.png b/doc/ci/img/pipelines_mini_graph.png new file mode 100644 index 00000000000..042c8ffeef5 Binary files /dev/null and b/doc/ci/img/pipelines_mini_graph.png differ diff --git a/doc/ci/img/pipelines_mini_graph_simple.png b/doc/ci/img/pipelines_mini_graph_simple.png new file mode 100644 index 00000000000..eb36c09b2d4 Binary files /dev/null and b/doc/ci/img/pipelines_mini_graph_simple.png differ diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index db92a4b0d80..79bae17b785 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -9,11 +9,17 @@ A pipeline is a group of [jobs][] that get executed in [stages][](batches). All of the jobs in a stage are executed in parallel (if there are enough concurrent [Runners]), and if they all succeed, the pipeline moves on to the next stage. If one of the jobs fails, the next stage is not (usually) -executed. +executed. You can access the pipelines page in your project's **Pipelines** tab. + +In the following image you can see that the pipeline consists of four stages +(`build`, `test`, `staging`, `production`) each one having one or more jobs. + +>**Note:** +GitLab capitalizes the stages' names when shown in the [pipeline graphs](#pipeline-graphs). ![Pipelines example](img/pipelines.png) -## Types of Pipelines +## Types of pipelines There are three types of pipelines that often use the single shorthand of "pipeline". People often talk about them as if each one is "the" pipeline, but really, they're just pieces of a single, comprehensive pipeline. @@ -23,7 +29,7 @@ There are three types of pipelines that often use the single shorthand of "pipel 2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production 3. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus. -## Development Workflows +## Development workflows Pipelines accommodate several development workflows: @@ -45,18 +51,116 @@ confused with a `build` job or `build` stage. Pipelines are defined in `.gitlab-ci.yml` by specifying [jobs] that run in [stages]. -See full [documentation](yaml/README.md#jobs). +See the reference [documentation for jobs](yaml/README.md#jobs). ## Seeing pipeline status -You can find the current and historical pipeline runs under **Pipelines** for -your project. +You can find the current and historical pipeline runs under your project's +**Pipelines** tab. Clicking on a pipeline will show the jobs that were run for +that pipeline. + +![Pipelines index page](img/pipelines_index.png) ## Seeing job status -Clicking on a pipeline will show the jobs that were run for that pipeline. +When you visit a single pipeline you can see the related jobs for that pipeline. Clicking on an individual job will show you its job trace, and allow you to -cancel the job, retry it, or erase the job trace. +cancel the job, retry it, or erase the job trace. + +![Pipelines example](img/pipelines.png) + +## Pipeline graphs + +> [Introduced][ce-5742] in GitLab 8.11. + +Pipelines can be complex structures with many sequential and parallel jobs. +To make it a little easier to see what is going on, you can view a graph +of a single pipeline and its status. + +A pipeline graph can be shown in two different ways depending on what page you +are on. + +--- + +The regular pipeline graph that shows the names of the jobs of each stage can +be found when you are on a [single pipeline page](#seeing-pipeline-status). + +![Pipelines example](img/pipelines.png) + +Then, there is the pipeline mini graph which takes less space and can give you a +quick glance if all jobs pass or something failed. The pipeline mini graph can +be found when you visit: + +- the pipelines index page +- a single commit page +- a merge request page + +That way, you can see all related jobs for a single commit and the net result +of each stage of your pipeline. This allows you to quickly see what failed and +fix it. Stages in pipeline mini graphs are collapsible. Hover your mouse over +them and click to expand their jobs. + +| **Mini graph** | **Mini graph expanded** | +| :------------: | :---------------------: | +| ![Pipelines mini graph](img/pipelines_mini_graph_simple.png) | ![Pipelines mini graph extended](img/pipelines_mini_graph.png) | + +### Grouping similar jobs in the pipeline graph + +> [Introduced][ce-6242] in GitLab 8.12. + +If you have many similar jobs, your pipeline graph becomes very long and hard +to read. For that reason, similar jobs can automatically be grouped together. +If the job names are formatted in certain ways, they will be collapsed into +a single group in regular pipeline graphs (not the mini graphs). +You'll know when a pipeline has grouped jobs if you don't see the retry or +cancel button inside them. Hovering over them will show the number of grouped +jobs. Click to expand them. + +![Grouped pipelines](img/pipelines_grouped.png) + +The basic requirements is that there are two numbers separated with one of +the following (you can even use them interchangeably): + +- a space +- a backslash (`/`) +- a colon (`:`) + +>**Note:** +More specifically, [it uses][regexp] this regular expression: `\d+[\s:\/\\]+\d+\s*`. + +The jobs will be ordered by comparing those two numbers from left to right. You +usually want the first to be the index and the second the total. + +For example, the following jobs will be grouped under a job named `test`: + +- `test 0 3` => `test` +- `test 1 3` => `test` +- `test 2 3` => `test` + +The following jobs will be grouped under a job named `test ruby`: + +- `test 1:2 ruby` => `test ruby` +- `test 2:2 ruby` => `test ruby` + +The following jobs will be grouped under a job named `test ruby` as well: + +- `1/3 test ruby` => `test ruby` +- `2/3 test ruby` => `test ruby` +- `3/3 test ruby` => `test ruby` + +### Manual actions from the pipeline graph + +> [Introduced][ce-7931] in GitLab 8.15. + +[Manual actions][manual] allow you to require manual interaction before moving +forward with a particular job in CI. Your entire pipeline can run automatically, +but the actual [deploy to production][env-manual] will require a click. + +You can do this straight from the pipeline graph. Just click on the play button +to execute that particular job. For example, in the image below, the `production` +stage has a job with a manual action. + +![Pipelines example](img/pipelines.png) ## How the pipeline duration is calculated @@ -96,7 +200,13 @@ respective link in the [Pipelines settings] page. [jobs]: #jobs [jobs-yaml]: yaml/README.md#jobs +[manual]: yaml/README.md#manual +[env-manual]: environments.md#manually-deploying-to-environments [stages]: yaml/README.md#stages [runners]: runners/README.html [pipelines settings]: ../user/project/pipelines/settings.md [triggers]: triggers/README.md +[ce-5742]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5742 +[ce-6242]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6242 +[ce-7931]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7931 +[regexp]: https://gitlab.com/gitlab-org/gitlab-ce/blob/2f3dc314f42dbd79813e6251792853bc231e69dd/app/models/commit_status.rb#L99 -- cgit v1.2.1 From e340fad0d23547f5122478d0cf8db2c1ffda09ba Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 28 Apr 2017 18:22:57 +0200 Subject: Document sorting of jobs in pipelines graph --- doc/ci/img/pipelines_mini_graph_sorting.png | Bin 0 -> 10742 bytes doc/ci/pipelines.md | 29 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 doc/ci/img/pipelines_mini_graph_sorting.png diff --git a/doc/ci/img/pipelines_mini_graph_sorting.png b/doc/ci/img/pipelines_mini_graph_sorting.png new file mode 100644 index 00000000000..3a4e5453360 Binary files /dev/null and b/doc/ci/img/pipelines_mini_graph_sorting.png differ diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 79bae17b785..5a2b61fb0cb 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -1,7 +1,6 @@ # Introduction to pipelines and jobs ->**Note:** -Introduced in GitLab 8.8. +> Introduced in GitLab 8.8. ## Pipelines @@ -162,6 +161,31 @@ stage has a job with a manual action. ![Pipelines example](img/pipelines.png) +### Ordering of jobs in pipeline graphs + +**Regular pipeline graph** + +In the single pipeline page, jobs are sorted by name. + +**Mini pipeline graph** + +> [Introduced][ce-9760] in GitLab 9.0. + +In the pipeline mini graphs, the jobs are sorted first by severity and then +by name. The order of severity is: + +- failed +- warning +- pending +- running +- manual +- canceled +- success +- skipped +- created + +![Pipeline mini graph sorting](img/pipelines_mini_graph_sorting.png) + ## How the pipeline duration is calculated Total running time for a given pipeline would exclude retries and pending @@ -209,4 +233,5 @@ respective link in the [Pipelines settings] page. [ce-5742]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5742 [ce-6242]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6242 [ce-7931]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7931 +[ce-9760]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9760 [regexp]: https://gitlab.com/gitlab-org/gitlab-ce/blob/2f3dc314f42dbd79813e6251792853bc231e69dd/app/models/commit_status.rb#L99 -- cgit v1.2.1 From 575d9027d952f22949c60539e9b6384b04c2c75b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 18 Apr 2017 14:01:15 -0400 Subject: Display check next to assigned user in dropdown --- app/assets/javascripts/users_select.js | 5 ++- app/assets/stylesheets/framework/dropdowns.scss | 3 +- app/views/shared/issuable/_sidebar.html.haml | 2 +- ...dding-along-left-side-of-assignees-dropdown.yml | 4 +++ spec/features/issues/form_spec.rb | 38 ++++++++++++++++++++++ 5 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/31057-unnecessary-padding-along-left-side-of-assignees-dropdown.yml diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 0344ce9ffb4..61157372f42 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -42,7 +42,6 @@ showAnyUser = $dropdown.data('any-user'); firstUser = $dropdown.data('first-user'); options.authorId = $dropdown.data('author-id'); - selectedId = $dropdown.data('selected'); defaultLabel = $dropdown.data('default-label'); issueURL = $dropdown.data('issueUpdate'); $selectbox = $dropdown.closest('.selectbox'); @@ -51,6 +50,7 @@ $value = $block.find('.value'); $collapsedSidebar = $block.find('.sidebar-collapsed-user'); $loading = $block.find('.block-loading').fadeOut(); + selectedId = $dropdown.data('selected') || showNullUser ? 0 : null; var updateIssueBoardsIssue = function () { $loading.removeClass('hidden').fadeIn(); @@ -208,9 +208,9 @@ page = $('body').data('page'); isIssueIndex = page === 'projects:issues:index'; isMRIndex = (page === page && page === 'projects:merge_requests:index'); + selectedId = user.id; if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { e.preventDefault(); - selectedId = user.id; if (selectedId === gon.current_user_id) { $('.assign-to-me-link').hide(); } else { @@ -221,7 +221,6 @@ if ($el.closest('.add-issues-modal').length) { gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id; } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { - selectedId = user.id; return Issuable.filterResults($dropdown.closest('form')); } else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 1313ea25c2a..73ded9f30d4 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -390,7 +390,8 @@ &::before { position: absolute; left: 6px; - top: 6px; + top: 50%; + transform: translateY(-50%); font: normal normal normal 14px/1 FontAwesome; font-size: inherit; text-rendering: auto; diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 2e0d6a129fb..06f5f2caa95 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -48,7 +48,7 @@ .selectbox.hide-collapsed = f.hidden_field 'assignee_id', value: issuable.assignee_id, id: 'issue_assignee_id' - = dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }) + = dropdown_tag('Select assignee', options: { toggle_class: 'js-user-search js-author-search', title: 'Assign to', filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: 'Search users', data: { first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_id]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true, selected: issuable.assignee_id } }) .block.milestone .sidebar-collapsed-icon diff --git a/changelogs/unreleased/31057-unnecessary-padding-along-left-side-of-assignees-dropdown.yml b/changelogs/unreleased/31057-unnecessary-padding-along-left-side-of-assignees-dropdown.yml new file mode 100644 index 00000000000..0d82bf878c7 --- /dev/null +++ b/changelogs/unreleased/31057-unnecessary-padding-along-left-side-of-assignees-dropdown.yml @@ -0,0 +1,4 @@ +--- +title: Show checkmark on current assignee in assignee dropdown +merge_request: 10767 +author: diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 755992069ff..21b8cf3add5 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe 'New/edit issue', feature: true, js: true do include GitlabRoutingHelper + include ActionView::Helpers::JavaScriptHelper let!(:project) { create(:project) } let!(:user) { create(:user)} @@ -105,6 +106,33 @@ describe 'New/edit issue', feature: true, js: true do expect(find('.js-label-select')).to have_content('Labels') end + + it 'correctly updates the selected user when changing assignee' do + click_button 'Assignee' + page.within '.dropdown-menu-user' do + click_link user.name + end + + expect(find('input[name="issue[assignee_id]"]', visible: false).value).to match(user.id.to_s) + + click_button user.name + + expect(find('.dropdown-menu-user a.is-active').first(:xpath, '..')['data-user-id']).to eq(user.id.to_s) + + # check the ::before pseudo element to ensure checkmark icon is present + expect(before_for_selector('.dropdown-menu-selectable a.is-active')).not_to eq('') + expect(before_for_selector('.dropdown-menu-selectable a:not(.is-active)')).to eq('') + + page.within '.dropdown-menu-user' do + click_link user2.name + end + + expect(find('input[name="issue[assignee_id]"]', visible: false).value).to match(user2.id.to_s) + + click_button user2.name + + expect(find('.dropdown-menu-user a.is-active').first(:xpath, '..')['data-user-id']).to eq(user2.id.to_s) + end end context 'edit issue' do @@ -154,4 +182,14 @@ describe 'New/edit issue', feature: true, js: true do end end end + + def before_for_selector(selector) + js = <<-JS.strip_heredoc + (function(selector) { + var el = document.querySelector(selector); + return window.getComputedStyle(el, '::before').getPropertyValue('content'); + })("#{escape_javascript(selector)}") + JS + page.evaluate_script(js) + end end -- cgit v1.2.1 From 357ab10da34102499a32ad569d9b58c8d5b97d17 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Apr 2017 20:59:20 +0100 Subject: Fixed some spinach & rspec tests --- app/assets/javascripts/deploy_keys/components/app.vue | 2 +- features/project/deploy_keys.feature | 6 ++++++ features/steps/project/deploy_keys.rb | 16 +++++++++------- spec/features/projects/deploy_keys_spec.rb | 12 ++++++++---- spec/serializers/deploy_key_entity_spec.rb | 9 ++++++++- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index ee2f85bde53..2ba5001cf56 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -77,7 +77,7 @@