diff options
author | Shinya Maeda <shinya@gitlab.com> | 2018-05-03 14:21:37 +0900 |
---|---|---|
committer | Shinya Maeda <shinya@gitlab.com> | 2018-05-03 14:21:37 +0900 |
commit | 90da0d4fe2a96248f864e003c1cf7f958f46c421 (patch) | |
tree | 3b40c7468d15aa7722a3104664838cccb747b25a | |
parent | b2d841851ecdd4a850ca2dc8d7f2f5b3d0b5a2bb (diff) | |
parent | 30464bceaac08bf7a8415df7d3b1e0ddf6182762 (diff) | |
download | gitlab-ce-90da0d4fe2a96248f864e003c1cf7f958f46c421.tar.gz |
Merge branch 'live-trace-v2' into live-trace-v2-efficient-destroy-all
150 files changed, 7226 insertions, 3881 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index e7db98858e8..29047c3ad65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.7.3 (2018-05-02) + +### Fixed (8 changes) + +- Fixed wrong avatar URL when the avatar is on object storage. !18092 +- Fix errors on pushing to an empty repository. !18462 +- Update doorkeeper to 4.3.2 to fix GitLab OAuth authentication. !18543 +- Ports omniauth-jwt gem onto GitLab OmniAuth Strategies suite. !18580 +- Fix redirection error for applications using OpenID. !18599 +- Fix commit trailer rendering when Gravatar is disabled. +- Fix file_store for artifacts and lfs when saving. +- Fix users not seeing labels from private groups when being a member of a child project. + + ## 10.7.2 (2018-04-25) ### Security (2 changes) @@ -184,6 +184,9 @@ gem 're2', '~> 1.1.1' gem 'version_sorter', '~> 2.1.0' +# User agent parsing +gem 'device_detector' + # Cache gem 'redis-rails', '~> 5.0.2' diff --git a/Gemfile.lock b/Gemfile.lock index 9b2c47587ee..f11df6a283e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -161,6 +161,7 @@ GEM activerecord (>= 3.2.0, < 5.1) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) + device_detector (1.0.0) devise (4.2.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -1026,6 +1027,7 @@ DEPENDENCIES database_cleaner (~> 1.5.0) deckar01-task_list (= 2.0.0) default_value_for (~> 3.0.0) + device_detector devise (~> 4.2) devise-two-factor (~> 3.0.0) diffy (~> 3.1.0) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index a0330cbdd02..10d5cb6a23f 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -304,12 +304,12 @@ GEM flowdock (~> 0.7) gitlab-grit (>= 2.4.1) multi_json - gitlab-gollum-lib (4.2.7.1) + gitlab-gollum-lib (4.2.7.2) gemojione (~> 3.2) github-markup (~> 1.6) gollum-grit_adapter (~> 1.0) nokogiri (>= 1.6.1, < 2.0) - rouge (~> 2.1) + rouge (~> 3.1) sanitize (~> 2.1) stringex (~> 2.6) gitlab-gollum-rugged_adapter (0.4.4) @@ -602,8 +602,6 @@ GEM atomic (>= 1.0.0) mysql2 peek - peek-performance_bar (1.3.1) - peek (>= 0.1.0) peek-pg (1.3.0) concurrent-ruby concurrent-ruby-ext @@ -752,7 +750,7 @@ GEM retriable (3.1.1) rinku (2.0.4) rotp (2.1.2) - rouge (2.2.1) + rouge (3.1.1) rqrcode (0.10.1) chunky_png (~> 1.0) rqrcode-rails3 (0.1.7) @@ -1134,7 +1132,6 @@ DEPENDENCIES peek (~> 1.0.1) peek-gc (~> 0.0.2) peek-mysql2 (~> 1.1.0) - peek-performance_bar (~> 1.3.0) peek-pg (~> 1.3.0) peek-rblineprof (~> 0.2.0) peek-redis (~> 1.2.0) @@ -1166,7 +1163,7 @@ DEPENDENCIES redis-rails (~> 5.0.2) request_store (~> 1.3) responders (~> 2.0) - rouge (~> 2.0) + rouge (~> 3.1) rqrcode-rails3 (~> 0.1.7) rspec-parameterized rspec-rails (~> 3.6.0) diff --git a/app/assets/javascripts/behaviors/gl_emoji.js b/app/assets/javascripts/behaviors/gl_emoji.js index 7e98e04303a..56293d5f96f 100644 --- a/app/assets/javascripts/behaviors/gl_emoji.js +++ b/app/assets/javascripts/behaviors/gl_emoji.js @@ -7,27 +7,24 @@ export default function installGlEmojiElement() { const GlEmojiElementProto = Object.create(HTMLElement.prototype); GlEmojiElementProto.createdCallback = function createdCallback() { const emojiUnicode = this.textContent.trim(); - const { - name, - unicodeVersion, - fallbackSrc, - fallbackSpriteClass, - } = this.dataset; + const { name, unicodeVersion, fallbackSrc, fallbackSpriteClass } = this.dataset; - const isEmojiUnicode = this.childNodes && Array.prototype.every.call( - this.childNodes, - childNode => childNode.nodeType === 3, - ); + const isEmojiUnicode = + this.childNodes && + Array.prototype.every.call(this.childNodes, childNode => childNode.nodeType === 3); const hasImageFallback = fallbackSrc && fallbackSrc.length > 0; const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0; - if ( - emojiUnicode && - isEmojiUnicode && - !isEmojiUnicodeSupported(emojiUnicode, unicodeVersion) - ) { + if (emojiUnicode && isEmojiUnicode && !isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)) { // CSS sprite fallback takes precedence over image fallback if (hasCssSpriteFalback) { + if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) { + const emojiSpriteLinkTag = document.createElement('link'); + emojiSpriteLinkTag.setAttribute('rel', 'stylesheet'); + emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path); + document.head.appendChild(emojiSpriteLinkTag); + gon.emoji_sprites_css_added = true; + } // IE 11 doesn't like adding multiple at once :( this.classList.add('emoji-icon'); this.classList.add(fallbackSpriteClass); diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js deleted file mode 100644 index 303a5bf4a53..00000000000 --- a/app/assets/javascripts/compare.js +++ /dev/null @@ -1,86 +0,0 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */ - -import $ from 'jquery'; -import { localTimeAgo } from './lib/utils/datetime_utility'; -import axios from './lib/utils/axios_utils'; - -export default class Compare { - constructor(opts) { - this.opts = opts; - this.source_loading = $(".js-source-loading"); - this.target_loading = $(".js-target-loading"); - $('.js-compare-dropdown').each((function(_this) { - return function(i, dropdown) { - var $dropdown; - $dropdown = $(dropdown); - return $dropdown.glDropdown({ - selectable: true, - fieldName: $dropdown.data('fieldName'), - filterable: true, - id: function(obj, $el) { - return $el.data('id'); - }, - toggleLabel: function(obj, $el) { - return $el.text().trim(); - }, - clicked: function(e, el) { - if ($dropdown.is('.js-target-branch')) { - return _this.getTargetHtml(); - } else if ($dropdown.is('.js-source-branch')) { - return _this.getSourceHtml(); - } else if ($dropdown.is('.js-target-project')) { - return _this.getTargetProject(); - } - } - }); - }; - })(this)); - this.initialState(); - } - - initialState() { - this.getSourceHtml(); - this.getTargetHtml(); - } - - getTargetProject() { - $('.mr_target_commit').empty(); - - return axios.get(this.opts.targetProjectUrl, { - params: { - target_project_id: $("input[name='merge_request[target_project_id]']").val(), - }, - }).then(({ data }) => { - $('.js-target-branch-dropdown .dropdown-content').html(data); - }); - } - - getSourceHtml() { - return this.constructor.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', { - ref: $("input[name='merge_request[source_branch]']").val() - }); - } - - getTargetHtml() { - return this.constructor.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', { - target_project_id: $("input[name='merge_request[target_project_id]']").val(), - ref: $("input[name='merge_request[target_branch]']").val() - }); - } - - static sendAjax(url, loading, target, params) { - const $target = $(target); - - loading.show(); - $target.empty(); - - return axios.get(url, { - params, - }).then(({ data }) => { - loading.hide(); - $target.html(data); - const className = '.' + $target[0].className.replace(' ', '.'); - localTimeAgo($('.js-timeago', className)); - }); - } -} diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js index 260c91cac24..9c88466e576 100644 --- a/app/assets/javascripts/compare_autocomplete.js +++ b/app/assets/javascripts/compare_autocomplete.js @@ -4,8 +4,9 @@ import $ from 'jquery'; import { __ } from './locale'; import axios from './lib/utils/axios_utils'; import flash from './flash'; +import { capitalizeFirstCharacter } from './lib/utils/text_utility'; -export default function initCompareAutocomplete() { +export default function initCompareAutocomplete(limitTo = null, clickHandler = () => {}) { $('.js-compare-dropdown').each(function() { var $dropdown, selected; $dropdown = $(this); @@ -15,14 +16,27 @@ export default function initCompareAutocomplete() { const $filterInput = $('input[type="search"]', $dropdownContainer); $dropdown.glDropdown({ data: function(term, callback) { - axios.get($dropdown.data('refsUrl'), { - params: { - ref: $dropdown.data('ref'), - search: term, - }, - }).then(({ data }) => { - callback(data); - }).catch(() => flash(__('Error fetching refs'))); + const params = { + ref: $dropdown.data('ref'), + search: term, + }; + + if (limitTo) { + params.find = limitTo; + } + + axios + .get($dropdown.data('refsUrl'), { + params, + }) + .then(({ data }) => { + if (limitTo) { + callback(data[capitalizeFirstCharacter(limitTo)] || []); + } else { + callback(data); + } + }) + .catch(() => flash(__('Error fetching refs'))); }, selectable: true, filterable: true, @@ -32,9 +46,15 @@ export default function initCompareAutocomplete() { renderRow: function(ref) { var link; if (ref.header != null) { - return $('<li />').addClass('dropdown-header').text(ref.header); + return $('<li />') + .addClass('dropdown-header') + .text(ref.header); } else { - link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref)); + link = $('<a />') + .attr('href', '#') + .addClass(ref === selected ? 'is-active' : '') + .text(ref) + .attr('data-ref', escape(ref)); return $('<li />').append(link); } }, @@ -43,9 +63,10 @@ export default function initCompareAutocomplete() { }, toggleLabel: function(obj, $el) { return $el.text().trim(); - } + }, + clicked: () => clickHandler($dropdown), }); - $filterInput.on('keyup', (e) => { + $filterInput.on('keyup', e => { const keyCode = e.keyCode || e.which; if (keyCode !== 13) return; const text = $filterInput.val(); @@ -54,7 +75,7 @@ export default function initCompareAutocomplete() { $dropdownContainer.removeClass('open'); }); - $dropdownContainer.on('click', '.dropdown-content a', (e) => { + $dropdownContainer.on('click', '.dropdown-content a', e => { $dropdown.prop('title', e.target.text.replace(/_+?/g, '-')); if ($dropdown.hasClass('has-tooltip')) { $dropdown.tooltip('fixTitle'); diff --git a/app/assets/javascripts/emoji/index.js b/app/assets/javascripts/emoji/index.js index dc7672560ea..cd8dff40b88 100644 --- a/app/assets/javascripts/emoji/index.js +++ b/app/assets/javascripts/emoji/index.js @@ -34,7 +34,7 @@ export function getEmojiCategoryMap() { symbols: [], flags: [], }; - Object.keys(emojiMap).forEach((name) => { + Object.keys(emojiMap).forEach(name => { const emoji = emojiMap[name]; if (emojiCategoryMap[emoji.category]) { emojiCategoryMap[emoji.category].push(name); @@ -79,7 +79,9 @@ export function glEmojiTag(inputName, options) { classList.push(fallbackSpriteClass); } const classAttribute = classList.length > 0 ? `class="${classList.join(' ')}"` : ''; - const fallbackSpriteAttribute = opts.sprite ? `data-fallback-sprite-class="${fallbackSpriteClass}"` : ''; + const fallbackSpriteAttribute = opts.sprite + ? `data-fallback-sprite-class="${fallbackSpriteClass}"` + : ''; let contents = emojiInfo.moji; if (opts.forceFallback && !opts.sprite) { contents = emojiImageTag(name, fallbackImageSrc); diff --git a/app/assets/javascripts/emoji/support/unicode_support_map.js b/app/assets/javascripts/emoji/support/unicode_support_map.js index c18d07dad43..8c1861c56db 100644 --- a/app/assets/javascripts/emoji/support/unicode_support_map.js +++ b/app/assets/javascripts/emoji/support/unicode_support_map.js @@ -54,7 +54,8 @@ const unicodeSupportTestMap = { function checkPixelInImageDataArray(pixelOffset, imageDataArray) { // `4 *` because RGBA const indexOffset = 4 * pixelOffset; - const hasColor = imageDataArray[indexOffset + 0] || + const hasColor = + imageDataArray[indexOffset + 0] || imageDataArray[indexOffset + 1] || imageDataArray[indexOffset + 2]; const isVisible = imageDataArray[indexOffset + 3]; @@ -75,23 +76,23 @@ const chromeVersion = chromeMatches && chromeMatches[1] && parseInt(chromeMatche const fontSize = 16; function generateUnicodeSupportMap(testMap) { const testMapKeys = Object.keys(testMap); - const numTestEntries = testMapKeys - .reduce((list, testKey) => list.concat(testMap[testKey]), []).length; + const numTestEntries = testMapKeys.reduce((list, testKey) => list.concat(testMap[testKey]), []) + .length; const canvas = document.createElement('canvas'); (window.gl || window).testEmojiUnicodeSupportMapCanvas = canvas; const ctx = canvas.getContext('2d'); - canvas.width = (2 * fontSize); - canvas.height = (numTestEntries * fontSize); + canvas.width = 2 * fontSize; + canvas.height = numTestEntries * fontSize; ctx.fillStyle = '#000000'; ctx.textBaseline = 'middle'; ctx.font = `${fontSize}px "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`; // Write each emoji to the canvas vertically let writeIndex = 0; - testMapKeys.forEach((testKey) => { + testMapKeys.forEach(testKey => { const testEntry = testMap[testKey]; - [].concat(testEntry).forEach((emojiUnicode) => { - ctx.fillText(emojiUnicode, 0, (writeIndex * fontSize) + (fontSize / 2)); + [].concat(testEntry).forEach(emojiUnicode => { + ctx.fillText(emojiUnicode, 0, writeIndex * fontSize + fontSize / 2); writeIndex += 1; }); }); @@ -99,29 +100,25 @@ function generateUnicodeSupportMap(testMap) { // Read from the canvas const resultMap = {}; let readIndex = 0; - testMapKeys.forEach((testKey) => { + testMapKeys.forEach(testKey => { const testEntry = testMap[testKey]; // This needs to be a `reduce` instead of `every` because we need to // keep the `readIndex` in sync from the writes by running all entries - const isTestSatisfied = [].concat(testEntry).reduce((isSatisfied) => { + const isTestSatisfied = [].concat(testEntry).reduce(isSatisfied => { // Sample along the vertical-middle for a couple of characters - const imageData = ctx.getImageData( - 0, - (readIndex * fontSize) + (fontSize / 2), - 2 * fontSize, - 1, - ).data; + const imageData = ctx.getImageData(0, readIndex * fontSize + fontSize / 2, 2 * fontSize, 1) + .data; let isValidEmoji = false; for (let currentPixel = 0; currentPixel < 64; currentPixel += 1) { const isLookingAtFirstChar = currentPixel < fontSize; - const isLookingAtSecondChar = currentPixel >= (fontSize + (fontSize / 2)); + const isLookingAtSecondChar = currentPixel >= fontSize + fontSize / 2; // Check for the emoji somewhere along the row if (isLookingAtFirstChar && checkPixelInImageDataArray(currentPixel, imageData)) { isValidEmoji = true; - // Check to see that nothing is rendered next to the first character - // to ensure that the ZWJ sequence rendered as one piece + // Check to see that nothing is rendered next to the first character + // to ensure that the ZWJ sequence rendered as one piece } else if (isLookingAtSecondChar && checkPixelInImageDataArray(currentPixel, imageData)) { isValidEmoji = false; break; @@ -170,7 +167,10 @@ export default function getUnicodeSupportMap() { if (isLocalStorageAvailable) { window.localStorage.setItem('gl-emoji-version', GL_EMOJI_VERSION); window.localStorage.setItem('gl-emoji-user-agent', navigator.userAgent); - window.localStorage.setItem('gl-emoji-unicode-support-map', JSON.stringify(unicodeSupportMap)); + window.localStorage.setItem( + 'gl-emoji-unicode-support-map', + JSON.stringify(unicodeSupportMap), + ); } } diff --git a/app/assets/javascripts/pages/projects/compare/index.js b/app/assets/javascripts/pages/projects/compare/index.js index d1c78bd61db..768da8fb236 100644 --- a/app/assets/javascripts/pages/projects/compare/index.js +++ b/app/assets/javascripts/pages/projects/compare/index.js @@ -1,3 +1,3 @@ import initCompareAutocomplete from '~/compare_autocomplete'; -document.addEventListener('DOMContentLoaded', initCompareAutocomplete); +document.addEventListener('DOMContentLoaded', () => initCompareAutocomplete()); diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js new file mode 100644 index 00000000000..46f3f55a400 --- /dev/null +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js @@ -0,0 +1,60 @@ +import $ from 'jquery'; +import { localTimeAgo } from '~/lib/utils/datetime_utility'; +import axios from '~/lib/utils/axios_utils'; +import initCompareAutocomplete from '~/compare_autocomplete'; +import initTargetProjectDropdown from './target_project_dropdown'; + +const updateCommitList = (url, $loadingIndicator, $commitList, params) => { + $loadingIndicator.show(); + $commitList.empty(); + + return axios + .get(url, { + params, + }) + .then(({ data }) => { + $loadingIndicator.hide(); + $commitList.html(data); + localTimeAgo($('.js-timeago', $commitList)); + }); +}; + +export default mrNewCompareNode => { + const { sourceBranchUrl, targetBranchUrl } = mrNewCompareNode.dataset; + initTargetProjectDropdown(); + + const updateSourceBranchCommitList = () => + updateCommitList( + sourceBranchUrl, + $(mrNewCompareNode).find('.js-source-loading'), + $(mrNewCompareNode).find('.mr_source_commit'), + { + ref: $(mrNewCompareNode) + .find("input[name='merge_request[source_branch]']") + .val(), + }, + ); + const updateTargetBranchCommitList = () => + updateCommitList( + targetBranchUrl, + $(mrNewCompareNode).find('.js-target-loading'), + $(mrNewCompareNode).find('.mr_target_commit'), + { + target_project_id: $(mrNewCompareNode) + .find("input[name='merge_request[target_project_id]']") + .val(), + ref: $(mrNewCompareNode) + .find("input[name='merge_request[target_branch]']") + .val(), + }, + ); + initCompareAutocomplete('branches', $dropdown => { + if ($dropdown.is('.js-target-branch')) { + updateTargetBranchCommitList(); + } else if ($dropdown.is('.js-source-branch')) { + updateSourceBranchCommitList(); + } + }); + updateSourceBranchCommitList(); + updateTargetBranchCommitList(); +}; diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js index 6c9afddefac..01a0b4870c1 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js @@ -1,18 +1,15 @@ -import Compare from '~/compare'; import MergeRequest from '~/merge_request'; import initPipelines from '~/commit/pipelines/pipelines_bundle'; +import initCompare from './compare'; document.addEventListener('DOMContentLoaded', () => { const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); if (mrNewCompareNode) { - new Compare({ // eslint-disable-line no-new - targetProjectUrl: mrNewCompareNode.dataset.targetProjectUrl, - sourceBranchUrl: mrNewCompareNode.dataset.sourceBranchUrl, - targetBranchUrl: mrNewCompareNode.dataset.targetBranchUrl, - }); + initCompare(mrNewCompareNode); } else { const mrNewSubmitNode = document.querySelector('.js-merge-request-new-submit'); - new MergeRequest({ // eslint-disable-line no-new + // eslint-disable-next-line no-new + new MergeRequest({ action: mrNewSubmitNode.dataset.mrSubmitAction, }); initPipelines(); diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/target_project_dropdown.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/target_project_dropdown.js new file mode 100644 index 00000000000..b72fe6681df --- /dev/null +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/target_project_dropdown.js @@ -0,0 +1,22 @@ +import $ from 'jquery'; + +export default () => { + const $targetProjectDropdown = $('.js-target-project'); + $targetProjectDropdown.glDropdown({ + selectable: true, + fieldName: $targetProjectDropdown.data('fieldName'), + filterable: true, + id(obj, $el) { + return $el.data('id'); + }, + toggleLabel(obj, $el) { + return $el.text().trim(); + }, + clicked({ $el }) { + $('.mr_target_commit').empty(); + const $targetBranchDropdown = $('.js-target-branch'); + $targetBranchDropdown.data('refsUrl', $el.data('refsUrl')); + $targetBranchDropdown.data('glDropdown').clearMenu(); + }, + }); +}; diff --git a/app/assets/javascripts/sidebar/components/time_tracking/no_tracking_pane.js b/app/assets/javascripts/sidebar/components/time_tracking/no_tracking_pane.js deleted file mode 100644 index 38da76c6771..00000000000 --- a/app/assets/javascripts/sidebar/components/time_tracking/no_tracking_pane.js +++ /dev/null @@ -1,10 +0,0 @@ -export default { - name: 'time-tracking-no-tracking-pane', - template: ` - <div class="time-tracking-no-tracking-pane"> - <span class="no-value"> - {{ __('No estimate or time spent') }} - </span> - </div> - `, -}; diff --git a/app/assets/javascripts/sidebar/components/time_tracking/no_tracking_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/no_tracking_pane.vue new file mode 100644 index 00000000000..9228184df5b --- /dev/null +++ b/app/assets/javascripts/sidebar/components/time_tracking/no_tracking_pane.vue @@ -0,0 +1,13 @@ +<script> +export default { + name: 'TimeTrackingNoTrackingPane', +}; +</script> + +<template> + <div class="time-tracking-no-tracking-pane"> + <span class="no-value"> + {{ __('No estimate or time spent') }} + </span> + </div> +</template> diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue index 5626cccc022..2e1d6e9643a 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js +++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue @@ -1,3 +1,4 @@ +<script> import $ from 'jquery'; import _ from 'underscore'; @@ -10,14 +11,17 @@ import Mediator from '../../sidebar_mediator'; import eventHub from '../../event_hub'; export default { + components: { + IssuableTimeTracker, + }, data() { return { mediator: new Mediator(), store: new Store(), }; }, - components: { - IssuableTimeTracker, + mounted() { + this.listenForQuickActions(); }, methods: { listenForQuickActions() { @@ -41,18 +45,17 @@ export default { } }, }, - mounted() { - this.listenForQuickActions(); - }, - template: ` - <div class="block"> - <issuable-time-tracker - :time_estimate="store.timeEstimate" - :time_spent="store.totalTimeSpent" - :human_time_estimate="store.humanTimeEstimate" - :human_time_spent="store.humanTotalTimeSpent" - :rootPath="store.rootPath" - /> - </div> - `, }; +</script> + +<template> + <div class="block"> + <issuable-time-tracker + :time_estimate="store.timeEstimate" + :time_spent="store.totalTimeSpent" + :human_time_estimate="store.humanTimeEstimate" + :human_time_spent="store.humanTotalTimeSpent" + :root-path="store.rootPath" + /> + </div> +</template> diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue index 71dca498b3d..9c003aa9f8a 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue @@ -2,7 +2,7 @@ import TimeTrackingHelpState from './help_state.vue'; import TimeTrackingCollapsedState from './collapsed_state.vue'; import timeTrackingSpentOnlyPane from './spent_only_pane'; -import timeTrackingNoTrackingPane from './no_tracking_pane'; +import TimeTrackingNoTrackingPane from './no_tracking_pane.vue'; import TimeTrackingEstimateOnlyPane from './estimate_only_pane.vue'; import TimeTrackingComparisonPane from './comparison_pane.vue'; @@ -14,7 +14,7 @@ export default { TimeTrackingCollapsedState, TimeTrackingEstimateOnlyPane, 'time-tracking-spent-only-pane': timeTrackingSpentOnlyPane, - 'time-tracking-no-tracking-pane': timeTrackingNoTrackingPane, + TimeTrackingNoTrackingPane, TimeTrackingComparisonPane, TimeTrackingHelpState, }, diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index 26eb4cffba3..3086e7d0fc9 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import Vue from 'vue'; -import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking'; +import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking.vue'; import SidebarAssignees from './components/assignees/sidebar_assignees.vue'; import ConfidentialIssueSidebar from './components/confidential/confidential_issue_sidebar.vue'; import SidebarMoveIssue from './lib/sidebar_move_issue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue index 44e1a616a19..fe2608e8212 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue @@ -1,25 +1,26 @@ +<script> import $ from 'jquery'; import statusIcon from '../mr_widget_status_icon.vue'; import tooltip from '../../../vue_shared/directives/tooltip'; import eventHub from '../../event_hub'; export default { - name: 'MRWidgetWIP', - props: { - mr: { type: Object, required: true }, - service: { type: Object, required: true }, + name: 'WorkInProgress', + components: { + statusIcon, }, directives: { tooltip, }, + props: { + mr: { type: Object, required: true }, + service: { type: Object, required: true }, + }, data() { return { isMakingRequest: false, }; }, - components: { - statusIcon, - }, methods: { removeWIP() { this.isMakingRequest = true; @@ -36,32 +37,40 @@ export default { }); }, }, - template: ` - <div class="mr-widget-body media"> - <status-icon status="warning" :show-disabled-button="Boolean(mr.removeWIPPath)" /> - <div class="media-body space-children"> - <span class="bold"> - This is a Work in Progress - <i - v-tooltip - class="fa fa-question-circle" - title="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged" - aria-label="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged"> - </i> - </span> - <button - v-if="mr.removeWIPPath" - @click="removeWIP" - :disabled="isMakingRequest" - type="button" - class="btn btn-default btn-xs js-remove-wip"> - <i - v-if="isMakingRequest" - class="fa fa-spinner fa-spin" - aria-hidden="true" /> - Resolve WIP status - </button> - </div> - </div> - `, }; +</script> + +<template> + <div class="mr-widget-body media"> + <status-icon + status="warning" + :show-disabled-button="Boolean(mr.removeWIPPath)" + /> + <div class="media-body space-children"> + <span class="bold"> + This is a Work in Progress + <i + v-tooltip + class="fa fa-question-circle" + title="When this merge request is ready, + remove the WIP: prefix from the title to allow it to be merged" + aria-label="When this merge request is ready, + remove the WIP: prefix from the title to allow it to be merged"> + </i> + </span> + <button + v-if="mr.removeWIPPath" + @click="removeWIP" + :disabled="isMakingRequest" + type="button" + class="btn btn-default btn-xs js-remove-wip"> + <i + v-if="isMakingRequest" + class="fa fa-spinner fa-spin" + aria-hidden="true"> + </i> + Resolve WIP status + </button> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js index 3b5c973e4a0..7f5f28091da 100644 --- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js +++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js @@ -21,7 +21,7 @@ export { default as MergedState } from './components/states/mr_widget_merged.vue export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue'; export { default as ClosedState } from './components/states/mr_widget_closed.vue'; export { default as MergingState } from './components/states/mr_widget_merging.vue'; -export { default as WipState } from './components/states/mr_widget_wip'; +export { default as WorkInProgressState } from './components/states/work_in_progress.vue'; export { default as ArchivedState } from './components/states/mr_widget_archived.vue'; export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue'; export { default as NothingToMergeState } from './components/states/nothing_to_merge.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js index 0be5d9e5a55..345f9ac1b4b 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js @@ -12,7 +12,7 @@ import { ClosedState, MergingState, RebaseState, - WipState, + WorkInProgressState, ArchivedState, ConflictsState, NothingToMergeState, @@ -220,7 +220,7 @@ export default { 'mr-widget-closed': ClosedState, 'mr-widget-merging': MergingState, 'mr-widget-failed-to-merge': FailedToMerge, - 'mr-widget-wip': WipState, + 'mr-widget-wip': WorkInProgressState, 'mr-widget-archived': ArchivedState, 'mr-widget-conflicts': ConflictsState, 'mr-widget-nothing-to-merge': NothingToMergeState, diff --git a/app/assets/stylesheets/emoji_sprites.scss b/app/assets/stylesheets/emoji_sprites.scss new file mode 100644 index 00000000000..8f6134c474b --- /dev/null +++ b/app/assets/stylesheets/emoji_sprites.scss @@ -0,0 +1,5403 @@ +// Automatic Prettier Formatting for this big file +// scss-lint:disable EmptyLineBetweenBlocks +.emoji-zzz { + background-position: 0 0; +} +.emoji-1234 { + background-position: -20px 0; +} +.emoji-1F627 { + background-position: 0 -20px; +} +.emoji-8ball { + background-position: -20px -20px; +} +.emoji-a { + background-position: -40px 0; +} +.emoji-ab { + background-position: -40px -20px; +} +.emoji-abc { + background-position: 0 -40px; +} +.emoji-abcd { + background-position: -20px -40px; +} +.emoji-accept { + background-position: -40px -40px; +} +.emoji-aerial_tramway { + background-position: -60px 0; +} +.emoji-airplane { + background-position: -60px -20px; +} +.emoji-airplane_arriving { + background-position: -60px -40px; +} +.emoji-airplane_departure { + background-position: 0 -60px; +} +.emoji-airplane_small { + background-position: -20px -60px; +} +.emoji-alarm_clock { + background-position: -40px -60px; +} +.emoji-alembic { + background-position: -60px -60px; +} +.emoji-alien { + background-position: -80px 0; +} +.emoji-ambulance { + background-position: -80px -20px; +} +.emoji-amphora { + background-position: -80px -40px; +} +.emoji-anchor { + background-position: -80px -60px; +} +.emoji-angel { + background-position: 0 -80px; +} +.emoji-angel_tone1 { + background-position: -20px -80px; +} +.emoji-angel_tone2 { + background-position: -40px -80px; +} +.emoji-angel_tone3 { + background-position: -60px -80px; +} +.emoji-angel_tone4 { + background-position: -80px -80px; +} +.emoji-angel_tone5 { + background-position: -100px 0; +} +.emoji-anger { + background-position: -100px -20px; +} +.emoji-anger_right { + background-position: -100px -40px; +} +.emoji-angry { + background-position: -100px -60px; +} +.emoji-ant { + background-position: -100px -80px; +} +.emoji-apple { + background-position: 0 -100px; +} +.emoji-aquarius { + background-position: -20px -100px; +} +.emoji-aries { + background-position: -40px -100px; +} +.emoji-arrow_backward { + background-position: -60px -100px; +} +.emoji-arrow_double_down { + background-position: -80px -100px; +} +.emoji-arrow_double_up { + background-position: -100px -100px; +} +.emoji-arrow_down { + background-position: -120px 0; +} +.emoji-arrow_down_small { + background-position: -120px -20px; +} +.emoji-arrow_forward { + background-position: -120px -40px; +} +.emoji-arrow_heading_down { + background-position: -120px -60px; +} +.emoji-arrow_heading_up { + background-position: -120px -80px; +} +.emoji-arrow_left { + background-position: -120px -100px; +} +.emoji-arrow_lower_left { + background-position: 0 -120px; +} +.emoji-arrow_lower_right { + background-position: -20px -120px; +} +.emoji-arrow_right { + background-position: -40px -120px; +} +.emoji-arrow_right_hook { + background-position: -60px -120px; +} +.emoji-arrow_up { + background-position: -80px -120px; +} +.emoji-arrow_up_down { + background-position: -100px -120px; +} +.emoji-arrow_up_small { + background-position: -120px -120px; +} +.emoji-arrow_upper_left { + background-position: -140px 0; +} +.emoji-arrow_upper_right { + background-position: -140px -20px; +} +.emoji-arrows_clockwise { + background-position: -140px -40px; +} +.emoji-arrows_counterclockwise { + background-position: -140px -60px; +} +.emoji-art { + background-position: -140px -80px; +} +.emoji-articulated_lorry { + background-position: -140px -100px; +} +.emoji-asterisk { + background-position: -140px -120px; +} +.emoji-astonished { + background-position: 0 -140px; +} +.emoji-athletic_shoe { + background-position: -20px -140px; +} +.emoji-atm { + background-position: -40px -140px; +} +.emoji-atom { + background-position: -60px -140px; +} +.emoji-avocado { + background-position: -80px -140px; +} +.emoji-b { + background-position: -100px -140px; +} +.emoji-baby { + background-position: -120px -140px; +} +.emoji-baby_bottle { + background-position: -140px -140px; +} +.emoji-baby_chick { + background-position: -160px 0; +} +.emoji-baby_symbol { + background-position: -160px -20px; +} +.emoji-baby_tone1 { + background-position: -160px -40px; +} +.emoji-baby_tone2 { + background-position: -160px -60px; +} +.emoji-baby_tone3 { + background-position: -160px -80px; +} +.emoji-baby_tone4 { + background-position: -160px -100px; +} +.emoji-baby_tone5 { + background-position: -160px -120px; +} +.emoji-back { + background-position: -160px -140px; +} +.emoji-bacon { + background-position: 0 -160px; +} +.emoji-badminton { + background-position: -20px -160px; +} +.emoji-baggage_claim { + background-position: -40px -160px; +} +.emoji-balloon { + background-position: -60px -160px; +} +.emoji-ballot_box { + background-position: -80px -160px; +} +.emoji-ballot_box_with_check { + background-position: -100px -160px; +} +.emoji-bamboo { + background-position: -120px -160px; +} +.emoji-banana { + background-position: -140px -160px; +} +.emoji-bangbang { + background-position: -160px -160px; +} +.emoji-bank { + background-position: -180px 0; +} +.emoji-bar_chart { + background-position: -180px -20px; +} +.emoji-barber { + background-position: -180px -40px; +} +.emoji-baseball { + background-position: -180px -60px; +} +.emoji-basketball { + background-position: -180px -80px; +} +.emoji-basketball_player { + background-position: -180px -100px; +} +.emoji-basketball_player_tone1 { + background-position: -180px -120px; +} +.emoji-basketball_player_tone2 { + background-position: -180px -140px; +} +.emoji-basketball_player_tone3 { + background-position: -180px -160px; +} +.emoji-basketball_player_tone4 { + background-position: 0 -180px; +} +.emoji-basketball_player_tone5 { + background-position: -20px -180px; +} +.emoji-bat { + background-position: -40px -180px; +} +.emoji-bath { + background-position: -60px -180px; +} +.emoji-bath_tone1 { + background-position: -80px -180px; +} +.emoji-bath_tone2 { + background-position: -100px -180px; +} +.emoji-bath_tone3 { + background-position: -120px -180px; +} +.emoji-bath_tone4 { + background-position: -140px -180px; +} +.emoji-bath_tone5 { + background-position: -160px -180px; +} +.emoji-bathtub { + background-position: -180px -180px; +} +.emoji-battery { + background-position: -200px 0; +} +.emoji-beach { + background-position: -200px -20px; +} +.emoji-beach_umbrella { + background-position: -200px -40px; +} +.emoji-bear { + background-position: -200px -60px; +} +.emoji-bed { + background-position: -200px -80px; +} +.emoji-bee { + background-position: -200px -100px; +} +.emoji-beer { + background-position: -200px -120px; +} +.emoji-beers { + background-position: -200px -140px; +} +.emoji-beetle { + background-position: -200px -160px; +} +.emoji-beginner { + background-position: -200px -180px; +} +.emoji-bell { + background-position: 0 -200px; +} +.emoji-bellhop { + background-position: -20px -200px; +} +.emoji-bento { + background-position: -40px -200px; +} +.emoji-bicyclist { + background-position: -60px -200px; +} +.emoji-bicyclist_tone1 { + background-position: -80px -200px; +} +.emoji-bicyclist_tone2 { + background-position: -100px -200px; +} +.emoji-bicyclist_tone3 { + background-position: -120px -200px; +} +.emoji-bicyclist_tone4 { + background-position: -140px -200px; +} +.emoji-bicyclist_tone5 { + background-position: -160px -200px; +} +.emoji-bike { + background-position: -180px -200px; +} +.emoji-bikini { + background-position: -200px -200px; +} +.emoji-biohazard { + background-position: -220px 0; +} +.emoji-bird { + background-position: -220px -20px; +} +.emoji-birthday { + background-position: -220px -40px; +} +.emoji-black_circle { + background-position: -220px -60px; +} +.emoji-black_heart { + background-position: -220px -80px; +} +.emoji-black_joker { + background-position: -220px -100px; +} +.emoji-black_large_square { + background-position: -220px -120px; +} +.emoji-black_medium_small_square { + background-position: -220px -140px; +} +.emoji-black_medium_square { + background-position: -220px -160px; +} +.emoji-black_nib { + background-position: -220px -180px; +} +.emoji-black_small_square { + background-position: -220px -200px; +} +.emoji-black_square_button { + background-position: 0 -220px; +} +.emoji-blossom { + background-position: -20px -220px; +} +.emoji-blowfish { + background-position: -40px -220px; +} +.emoji-blue_book { + background-position: -60px -220px; +} +.emoji-blue_car { + background-position: -80px -220px; +} +.emoji-blue_heart { + background-position: -100px -220px; +} +.emoji-blush { + background-position: -120px -220px; +} +.emoji-boar { + background-position: -140px -220px; +} +.emoji-bomb { + background-position: -160px -220px; +} +.emoji-book { + background-position: -180px -220px; +} +.emoji-bookmark { + background-position: -200px -220px; +} +.emoji-bookmark_tabs { + background-position: -220px -220px; +} +.emoji-books { + background-position: -240px 0; +} +.emoji-boom { + background-position: -240px -20px; +} +.emoji-boot { + background-position: -240px -40px; +} +.emoji-bouquet { + background-position: -240px -60px; +} +.emoji-bow { + background-position: -240px -80px; +} +.emoji-bow_and_arrow { + background-position: -240px -100px; +} +.emoji-bow_tone1 { + background-position: -240px -120px; +} +.emoji-bow_tone2 { + background-position: -240px -140px; +} +.emoji-bow_tone3 { + background-position: -240px -160px; +} +.emoji-bow_tone4 { + background-position: -240px -180px; +} +.emoji-bow_tone5 { + background-position: -240px -200px; +} +.emoji-bowling { + background-position: -240px -220px; +} +.emoji-boxing_glove { + background-position: 0 -240px; +} +.emoji-boy { + background-position: -20px -240px; +} +.emoji-boy_tone1 { + background-position: -40px -240px; +} +.emoji-boy_tone2 { + background-position: -60px -240px; +} +.emoji-boy_tone3 { + background-position: -80px -240px; +} +.emoji-boy_tone4 { + background-position: -100px -240px; +} +.emoji-boy_tone5 { + background-position: -120px -240px; +} +.emoji-bread { + background-position: -140px -240px; +} +.emoji-bride_with_veil { + background-position: -160px -240px; +} +.emoji-bride_with_veil_tone1 { + background-position: -180px -240px; +} +.emoji-bride_with_veil_tone2 { + background-position: -200px -240px; +} +.emoji-bride_with_veil_tone3 { + background-position: -220px -240px; +} +.emoji-bride_with_veil_tone4 { + background-position: -240px -240px; +} +.emoji-bride_with_veil_tone5 { + background-position: -260px 0; +} +.emoji-bridge_at_night { + background-position: -260px -20px; +} +.emoji-briefcase { + background-position: -260px -40px; +} +.emoji-broken_heart { + background-position: -260px -60px; +} +.emoji-bug { + background-position: -260px -80px; +} +.emoji-bulb { + background-position: -260px -100px; +} +.emoji-bullettrain_front { + background-position: -260px -120px; +} +.emoji-bullettrain_side { + background-position: -260px -140px; +} +.emoji-burrito { + background-position: -260px -160px; +} +.emoji-bus { + background-position: -260px -180px; +} +.emoji-busstop { + background-position: -260px -200px; +} +.emoji-bust_in_silhouette { + background-position: -260px -220px; +} +.emoji-busts_in_silhouette { + background-position: -260px -240px; +} +.emoji-butterfly { + background-position: 0 -260px; +} +.emoji-cactus { + background-position: -20px -260px; +} +.emoji-cake { + background-position: -40px -260px; +} +.emoji-calendar { + background-position: -60px -260px; +} +.emoji-calendar_spiral { + background-position: -80px -260px; +} +.emoji-call_me { + background-position: -100px -260px; +} +.emoji-call_me_tone1 { + background-position: -120px -260px; +} +.emoji-call_me_tone2 { + background-position: -140px -260px; +} +.emoji-call_me_tone3 { + background-position: -160px -260px; +} +.emoji-call_me_tone4 { + background-position: -180px -260px; +} +.emoji-call_me_tone5 { + background-position: -200px -260px; +} +.emoji-calling { + background-position: -220px -260px; +} +.emoji-camel { + background-position: -240px -260px; +} +.emoji-camera { + background-position: -260px -260px; +} +.emoji-camera_with_flash { + background-position: -280px 0; +} +.emoji-camping { + background-position: -280px -20px; +} +.emoji-cancer { + background-position: -280px -40px; +} +.emoji-candle { + background-position: -280px -60px; +} +.emoji-candy { + background-position: -280px -80px; +} +.emoji-canoe { + background-position: -280px -100px; +} +.emoji-capital_abcd { + background-position: -280px -120px; +} +.emoji-capricorn { + background-position: -280px -140px; +} +.emoji-card_box { + background-position: -280px -160px; +} +.emoji-card_index { + background-position: -280px -180px; +} +.emoji-carousel_horse { + background-position: -280px -200px; +} +.emoji-carrot { + background-position: -280px -220px; +} +.emoji-cartwheel { + background-position: -280px -240px; +} +.emoji-cartwheel_tone1 { + background-position: -280px -260px; +} +.emoji-cartwheel_tone2 { + background-position: 0 -280px; +} +.emoji-cartwheel_tone3 { + background-position: -20px -280px; +} +.emoji-cartwheel_tone4 { + background-position: -40px -280px; +} +.emoji-cartwheel_tone5 { + background-position: -60px -280px; +} +.emoji-cat { + background-position: -80px -280px; +} +.emoji-cat2 { + background-position: -100px -280px; +} +.emoji-cd { + background-position: -120px -280px; +} +.emoji-chains { + background-position: -140px -280px; +} +.emoji-champagne { + background-position: -160px -280px; +} +.emoji-champagne_glass { + background-position: -180px -280px; +} +.emoji-chart { + background-position: -200px -280px; +} +.emoji-chart_with_downwards_trend { + background-position: -220px -280px; +} +.emoji-chart_with_upwards_trend { + background-position: -240px -280px; +} +.emoji-checkered_flag { + background-position: -260px -280px; +} +.emoji-cheese { + background-position: -280px -280px; +} +.emoji-cherries { + background-position: -300px 0; +} +.emoji-cherry_blossom { + background-position: -300px -20px; +} +.emoji-chestnut { + background-position: -300px -40px; +} +.emoji-chicken { + background-position: -300px -60px; +} +.emoji-children_crossing { + background-position: -300px -80px; +} +.emoji-chipmunk { + background-position: -300px -100px; +} +.emoji-chocolate_bar { + background-position: -300px -120px; +} +.emoji-christmas_tree { + background-position: -300px -140px; +} +.emoji-church { + background-position: -300px -160px; +} +.emoji-cinema { + background-position: -300px -180px; +} +.emoji-circus_tent { + background-position: -300px -200px; +} +.emoji-city_dusk { + background-position: -300px -220px; +} +.emoji-city_sunset { + background-position: -300px -240px; +} +.emoji-cityscape { + background-position: -300px -260px; +} +.emoji-cl { + background-position: -300px -280px; +} +.emoji-clap { + background-position: 0 -300px; +} +.emoji-clap_tone1 { + background-position: -20px -300px; +} +.emoji-clap_tone2 { + background-position: -40px -300px; +} +.emoji-clap_tone3 { + background-position: -60px -300px; +} +.emoji-clap_tone4 { + background-position: -80px -300px; +} +.emoji-clap_tone5 { + background-position: -100px -300px; +} +.emoji-clapper { + background-position: -120px -300px; +} +.emoji-classical_building { + background-position: -140px -300px; +} +.emoji-clipboard { + background-position: -160px -300px; +} +.emoji-clock { + background-position: -180px -300px; +} +.emoji-clock1 { + background-position: -200px -300px; +} +.emoji-clock10 { + background-position: -220px -300px; +} +.emoji-clock1030 { + background-position: -240px -300px; +} +.emoji-clock11 { + background-position: -260px -300px; +} +.emoji-clock1130 { + background-position: -280px -300px; +} +.emoji-clock12 { + background-position: -300px -300px; +} +.emoji-clock1230 { + background-position: -320px 0; +} +.emoji-clock130 { + background-position: -320px -20px; +} +.emoji-clock2 { + background-position: -320px -40px; +} +.emoji-clock230 { + background-position: -320px -60px; +} +.emoji-clock3 { + background-position: -320px -80px; +} +.emoji-clock330 { + background-position: -320px -100px; +} +.emoji-clock4 { + background-position: -320px -120px; +} +.emoji-clock430 { + background-position: -320px -140px; +} +.emoji-clock5 { + background-position: -320px -160px; +} +.emoji-clock530 { + background-position: -320px -180px; +} +.emoji-clock6 { + background-position: -320px -200px; +} +.emoji-clock630 { + background-position: -320px -220px; +} +.emoji-clock7 { + background-position: -320px -240px; +} +.emoji-clock730 { + background-position: -320px -260px; +} +.emoji-clock8 { + background-position: -320px -280px; +} +.emoji-clock830 { + background-position: -320px -300px; +} +.emoji-clock9 { + background-position: 0 -320px; +} +.emoji-clock930 { + background-position: -20px -320px; +} +.emoji-closed_book { + background-position: -40px -320px; +} +.emoji-closed_lock_with_key { + background-position: -60px -320px; +} +.emoji-closed_umbrella { + background-position: -80px -320px; +} +.emoji-cloud { + background-position: -100px -320px; +} +.emoji-cloud_lightning { + background-position: -120px -320px; +} +.emoji-cloud_rain { + background-position: -140px -320px; +} +.emoji-cloud_snow { + background-position: -160px -320px; +} +.emoji-cloud_tornado { + background-position: -180px -320px; +} +.emoji-clown { + background-position: -200px -320px; +} +.emoji-clubs { + background-position: -220px -320px; +} +.emoji-cocktail { + background-position: -240px -320px; +} +.emoji-coffee { + background-position: -260px -320px; +} +.emoji-coffin { + background-position: -280px -320px; +} +.emoji-cold_sweat { + background-position: -300px -320px; +} +.emoji-comet { + background-position: -320px -320px; +} +.emoji-compression { + background-position: -340px 0; +} +.emoji-computer { + background-position: -340px -20px; +} +.emoji-confetti_ball { + background-position: -340px -40px; +} +.emoji-confounded { + background-position: -340px -60px; +} +.emoji-confused { + background-position: -340px -80px; +} +.emoji-congratulations { + background-position: -340px -100px; +} +.emoji-construction { + background-position: -340px -120px; +} +.emoji-construction_site { + background-position: -340px -140px; +} +.emoji-construction_worker { + background-position: -340px -160px; +} +.emoji-construction_worker_tone1 { + background-position: -340px -180px; +} +.emoji-construction_worker_tone2 { + background-position: -340px -200px; +} +.emoji-construction_worker_tone3 { + background-position: -340px -220px; +} +.emoji-construction_worker_tone4 { + background-position: -340px -240px; +} +.emoji-construction_worker_tone5 { + background-position: -340px -260px; +} +.emoji-control_knobs { + background-position: -340px -280px; +} +.emoji-convenience_store { + background-position: -340px -300px; +} +.emoji-cookie { + background-position: -340px -320px; +} +.emoji-cooking { + background-position: 0 -340px; +} +.emoji-cool { + background-position: -20px -340px; +} +.emoji-cop { + background-position: -40px -340px; +} +.emoji-cop_tone1 { + background-position: -60px -340px; +} +.emoji-cop_tone2 { + background-position: -80px -340px; +} +.emoji-cop_tone3 { + background-position: -100px -340px; +} +.emoji-cop_tone4 { + background-position: -120px -340px; +} +.emoji-cop_tone5 { + background-position: -140px -340px; +} +.emoji-copyright { + background-position: -160px -340px; +} +.emoji-corn { + background-position: -180px -340px; +} +.emoji-couch { + background-position: -200px -340px; +} +.emoji-couple { + background-position: -220px -340px; +} +.emoji-couple_mm { + background-position: -240px -340px; +} +.emoji-couple_with_heart { + background-position: -260px -340px; +} +.emoji-couple_ww { + background-position: -280px -340px; +} +.emoji-couplekiss { + background-position: -300px -340px; +} +.emoji-cow { + background-position: -320px -340px; +} +.emoji-cow2 { + background-position: -340px -340px; +} +.emoji-cowboy { + background-position: -360px 0; +} +.emoji-crab { + background-position: -360px -20px; +} +.emoji-crayon { + background-position: -360px -40px; +} +.emoji-credit_card { + background-position: -360px -60px; +} +.emoji-crescent_moon { + background-position: -360px -80px; +} +.emoji-cricket { + background-position: -360px -100px; +} +.emoji-crocodile { + background-position: -360px -120px; +} +.emoji-croissant { + background-position: -360px -140px; +} +.emoji-cross { + background-position: -360px -160px; +} +.emoji-crossed_flags { + background-position: -360px -180px; +} +.emoji-crossed_swords { + background-position: -360px -200px; +} +.emoji-crown { + background-position: -360px -220px; +} +.emoji-cruise_ship { + background-position: -360px -240px; +} +.emoji-cry { + background-position: -360px -260px; +} +.emoji-crying_cat_face { + background-position: -360px -280px; +} +.emoji-crystal_ball { + background-position: -360px -300px; +} +.emoji-cucumber { + background-position: -360px -320px; +} +.emoji-cupid { + background-position: -360px -340px; +} +.emoji-curly_loop { + background-position: 0 -360px; +} +.emoji-currency_exchange { + background-position: -20px -360px; +} +.emoji-curry { + background-position: -40px -360px; +} +.emoji-custard { + background-position: -60px -360px; +} +.emoji-customs { + background-position: -80px -360px; +} +.emoji-cyclone { + background-position: -100px -360px; +} +.emoji-dagger { + background-position: -120px -360px; +} +.emoji-dancer { + background-position: -140px -360px; +} +.emoji-dancer_tone1 { + background-position: -160px -360px; +} +.emoji-dancer_tone2 { + background-position: -180px -360px; +} +.emoji-dancer_tone3 { + background-position: -200px -360px; +} +.emoji-dancer_tone4 { + background-position: -220px -360px; +} +.emoji-dancer_tone5 { + background-position: -240px -360px; +} +.emoji-dancers { + background-position: -260px -360px; +} +.emoji-dango { + background-position: -280px -360px; +} +.emoji-dark_sunglasses { + background-position: -300px -360px; +} +.emoji-dart { + background-position: -320px -360px; +} +.emoji-dash { + background-position: -340px -360px; +} +.emoji-date { + background-position: -360px -360px; +} +.emoji-deciduous_tree { + background-position: -380px 0; +} +.emoji-deer { + background-position: -380px -20px; +} +.emoji-department_store { + background-position: -380px -40px; +} +.emoji-desert { + background-position: -380px -60px; +} +.emoji-desktop { + background-position: -380px -80px; +} +.emoji-diamond_shape_with_a_dot_inside { + background-position: -380px -100px; +} +.emoji-diamonds { + background-position: -380px -120px; +} +.emoji-disappointed { + background-position: -380px -140px; +} +.emoji-disappointed_relieved { + background-position: -380px -160px; +} +.emoji-dividers { + background-position: -380px -180px; +} +.emoji-dizzy { + background-position: -380px -200px; +} +.emoji-dizzy_face { + background-position: -380px -220px; +} +.emoji-do_not_litter { + background-position: -380px -240px; +} +.emoji-dog { + background-position: -380px -260px; +} +.emoji-dog2 { + background-position: -380px -280px; +} +.emoji-dollar { + background-position: -380px -300px; +} +.emoji-dolls { + background-position: -380px -320px; +} +.emoji-dolphin { + background-position: -380px -340px; +} +.emoji-door { + background-position: -380px -360px; +} +.emoji-doughnut { + background-position: 0 -380px; +} +.emoji-dove { + background-position: -20px -380px; +} +.emoji-dragon { + background-position: -40px -380px; +} +.emoji-dragon_face { + background-position: -60px -380px; +} +.emoji-dress { + background-position: -80px -380px; +} +.emoji-dromedary_camel { + background-position: -100px -380px; +} +.emoji-drooling_face { + background-position: -120px -380px; +} +.emoji-droplet { + background-position: -140px -380px; +} +.emoji-drum { + background-position: -160px -380px; +} +.emoji-duck { + background-position: -180px -380px; +} +.emoji-dvd { + background-position: -200px -380px; +} +.emoji-e-mail { + background-position: -220px -380px; +} +.emoji-eagle { + background-position: -240px -380px; +} +.emoji-ear { + background-position: -260px -380px; +} +.emoji-ear_of_rice { + background-position: -280px -380px; +} +.emoji-ear_tone1 { + background-position: -300px -380px; +} +.emoji-ear_tone2 { + background-position: -320px -380px; +} +.emoji-ear_tone3 { + background-position: -340px -380px; +} +.emoji-ear_tone4 { + background-position: -360px -380px; +} +.emoji-ear_tone5 { + background-position: -380px -380px; +} +.emoji-earth_africa { + background-position: -400px 0; +} +.emoji-earth_americas { + background-position: -400px -20px; +} +.emoji-earth_asia { + background-position: -400px -40px; +} +.emoji-egg { + background-position: -400px -60px; +} +.emoji-eggplant { + background-position: -400px -80px; +} +.emoji-eight { + background-position: -400px -100px; +} +.emoji-eight_pointed_black_star { + background-position: -400px -120px; +} +.emoji-eight_spoked_asterisk { + background-position: -400px -140px; +} +.emoji-eject { + background-position: -400px -160px; +} +.emoji-electric_plug { + background-position: -400px -180px; +} +.emoji-elephant { + background-position: -400px -200px; +} +.emoji-end { + background-position: -400px -220px; +} +.emoji-envelope { + background-position: -400px -240px; +} +.emoji-envelope_with_arrow { + background-position: -400px -260px; +} +.emoji-euro { + background-position: -400px -280px; +} +.emoji-european_castle { + background-position: -400px -300px; +} +.emoji-european_post_office { + background-position: -400px -320px; +} +.emoji-evergreen_tree { + background-position: -400px -340px; +} +.emoji-exclamation { + background-position: -400px -360px; +} +.emoji-expressionless { + background-position: -400px -380px; +} +.emoji-eye { + background-position: 0 -400px; +} +.emoji-eye_in_speech_bubble { + background-position: -20px -400px; +} +.emoji-eyeglasses { + background-position: -40px -400px; +} +.emoji-eyes { + background-position: -60px -400px; +} +.emoji-face_palm { + background-position: -80px -400px; +} +.emoji-face_palm_tone1 { + background-position: -100px -400px; +} +.emoji-face_palm_tone2 { + background-position: -120px -400px; +} +.emoji-face_palm_tone3 { + background-position: -140px -400px; +} +.emoji-face_palm_tone4 { + background-position: -160px -400px; +} +.emoji-face_palm_tone5 { + background-position: -180px -400px; +} +.emoji-factory { + background-position: -200px -400px; +} +.emoji-fallen_leaf { + background-position: -220px -400px; +} +.emoji-family { + background-position: -240px -400px; +} +.emoji-family_mmb { + background-position: -260px -400px; +} +.emoji-family_mmbb { + background-position: -280px -400px; +} +.emoji-family_mmg { + background-position: -300px -400px; +} +.emoji-family_mmgb { + background-position: -320px -400px; +} +.emoji-family_mmgg { + background-position: -340px -400px; +} +.emoji-family_mwbb { + background-position: -360px -400px; +} +.emoji-family_mwg { + background-position: -380px -400px; +} +.emoji-family_mwgb { + background-position: -400px -400px; +} +.emoji-family_mwgg { + background-position: -420px 0; +} +.emoji-family_wwb { + background-position: -420px -20px; +} +.emoji-family_wwbb { + background-position: -420px -40px; +} +.emoji-family_wwg { + background-position: -420px -60px; +} +.emoji-family_wwgb { + background-position: -420px -80px; +} +.emoji-family_wwgg { + background-position: -420px -100px; +} +.emoji-fast_forward { + background-position: -420px -120px; +} +.emoji-fax { + background-position: -420px -140px; +} +.emoji-fearful { + background-position: -420px -160px; +} +.emoji-feet { + background-position: -420px -180px; +} +.emoji-fencer { + background-position: -420px -200px; +} +.emoji-ferris_wheel { + background-position: -420px -220px; +} +.emoji-ferry { + background-position: -420px -240px; +} +.emoji-field_hockey { + background-position: -420px -260px; +} +.emoji-file_cabinet { + background-position: -420px -280px; +} +.emoji-file_folder { + background-position: -420px -300px; +} +.emoji-film_frames { + background-position: -420px -320px; +} +.emoji-fingers_crossed { + background-position: -420px -340px; +} +.emoji-fingers_crossed_tone1 { + background-position: -420px -360px; +} +.emoji-fingers_crossed_tone2 { + background-position: -420px -380px; +} +.emoji-fingers_crossed_tone3 { + background-position: -420px -400px; +} +.emoji-fingers_crossed_tone4 { + background-position: 0 -420px; +} +.emoji-fingers_crossed_tone5 { + background-position: -20px -420px; +} +.emoji-fire { + background-position: -40px -420px; +} +.emoji-fire_engine { + background-position: -60px -420px; +} +.emoji-fireworks { + background-position: -80px -420px; +} +.emoji-first_place { + background-position: -100px -420px; +} +.emoji-first_quarter_moon { + background-position: -120px -420px; +} +.emoji-first_quarter_moon_with_face { + background-position: -140px -420px; +} +.emoji-fish { + background-position: -160px -420px; +} +.emoji-fish_cake { + background-position: -180px -420px; +} +.emoji-fishing_pole_and_fish { + background-position: -200px -420px; +} +.emoji-fist { + background-position: -220px -420px; +} +.emoji-fist_tone1 { + background-position: -240px -420px; +} +.emoji-fist_tone2 { + background-position: -260px -420px; +} +.emoji-fist_tone3 { + background-position: -280px -420px; +} +.emoji-fist_tone4 { + background-position: -300px -420px; +} +.emoji-fist_tone5 { + background-position: -320px -420px; +} +.emoji-five { + background-position: -340px -420px; +} +.emoji-flag_ac { + background-position: -360px -420px; +} +.emoji-flag_ad { + background-position: -380px -420px; +} +.emoji-flag_ae { + background-position: -400px -420px; +} +.emoji-flag_af { + background-position: -420px -420px; +} +.emoji-flag_ag { + background-position: -440px 0; +} +.emoji-flag_ai { + background-position: -440px -20px; +} +.emoji-flag_al { + background-position: -440px -40px; +} +.emoji-flag_am { + background-position: -440px -60px; +} +.emoji-flag_ao { + background-position: -440px -80px; +} +.emoji-flag_aq { + background-position: -440px -100px; +} +.emoji-flag_ar { + background-position: -440px -120px; +} +.emoji-flag_as { + background-position: -440px -140px; +} +.emoji-flag_at { + background-position: -440px -160px; +} +.emoji-flag_au { + background-position: -440px -180px; +} +.emoji-flag_aw { + background-position: -440px -200px; +} +.emoji-flag_ax { + background-position: -440px -220px; +} +.emoji-flag_az { + background-position: -440px -240px; +} +.emoji-flag_ba { + background-position: -440px -260px; +} +.emoji-flag_bb { + background-position: -440px -280px; +} +.emoji-flag_bd { + background-position: -440px -300px; +} +.emoji-flag_be { + background-position: -440px -320px; +} +.emoji-flag_bf { + background-position: -440px -340px; +} +.emoji-flag_bg { + background-position: -440px -360px; +} +.emoji-flag_bh { + background-position: -440px -380px; +} +.emoji-flag_bi { + background-position: -440px -400px; +} +.emoji-flag_bj { + background-position: -440px -420px; +} +.emoji-flag_bl { + background-position: 0 -440px; +} +.emoji-flag_black { + background-position: -20px -440px; +} +.emoji-flag_bm { + background-position: -40px -440px; +} +.emoji-flag_bn { + background-position: -60px -440px; +} +.emoji-flag_bo { + background-position: -80px -440px; +} +.emoji-flag_bq { + background-position: -100px -440px; +} +.emoji-flag_br { + background-position: -120px -440px; +} +.emoji-flag_bs { + background-position: -140px -440px; +} +.emoji-flag_bt { + background-position: -160px -440px; +} +.emoji-flag_bv { + background-position: -180px -440px; +} +.emoji-flag_bw { + background-position: -200px -440px; +} +.emoji-flag_by { + background-position: -220px -440px; +} +.emoji-flag_bz { + background-position: -240px -440px; +} +.emoji-flag_ca { + background-position: -260px -440px; +} +.emoji-flag_cc { + background-position: -280px -440px; +} +.emoji-flag_cd { + background-position: -300px -440px; +} +.emoji-flag_cf { + background-position: -320px -440px; +} +.emoji-flag_cg { + background-position: -340px -440px; +} +.emoji-flag_ch { + background-position: -360px -440px; +} +.emoji-flag_ci { + background-position: -380px -440px; +} +.emoji-flag_ck { + background-position: -400px -440px; +} +.emoji-flag_cl { + background-position: -420px -440px; +} +.emoji-flag_cm { + background-position: -440px -440px; +} +.emoji-flag_cn { + background-position: -460px 0; +} +.emoji-flag_co { + background-position: -460px -20px; +} +.emoji-flag_cp { + background-position: -460px -40px; +} +.emoji-flag_cr { + background-position: -460px -60px; +} +.emoji-flag_cu { + background-position: -460px -80px; +} +.emoji-flag_cv { + background-position: -460px -100px; +} +.emoji-flag_cw { + background-position: -460px -120px; +} +.emoji-flag_cx { + background-position: -460px -140px; +} +.emoji-flag_cy { + background-position: -460px -160px; +} +.emoji-flag_cz { + background-position: -460px -180px; +} +.emoji-flag_de { + background-position: -460px -200px; +} +.emoji-flag_dg { + background-position: -460px -220px; +} +.emoji-flag_dj { + background-position: -460px -240px; +} +.emoji-flag_dk { + background-position: -460px -260px; +} +.emoji-flag_dm { + background-position: -460px -280px; +} +.emoji-flag_do { + background-position: -460px -300px; +} +.emoji-flag_dz { + background-position: -460px -320px; +} +.emoji-flag_ea { + background-position: -460px -340px; +} +.emoji-flag_ec { + background-position: -460px -360px; +} +.emoji-flag_ee { + background-position: -460px -380px; +} +.emoji-flag_eg { + background-position: -460px -400px; +} +.emoji-flag_eh { + background-position: -460px -420px; +} +.emoji-flag_er { + background-position: -460px -440px; +} +.emoji-flag_es { + background-position: 0 -460px; +} +.emoji-flag_et { + background-position: -20px -460px; +} +.emoji-flag_eu { + background-position: -40px -460px; +} +.emoji-flag_fi { + background-position: -60px -460px; +} +.emoji-flag_fj { + background-position: -80px -460px; +} +.emoji-flag_fk { + background-position: -100px -460px; +} +.emoji-flag_fm { + background-position: -120px -460px; +} +.emoji-flag_fo { + background-position: -140px -460px; +} +.emoji-flag_fr { + background-position: -160px -460px; +} +.emoji-flag_ga { + background-position: -180px -460px; +} +.emoji-flag_gb { + background-position: -200px -460px; +} +.emoji-flag_gd { + background-position: -220px -460px; +} +.emoji-flag_ge { + background-position: -240px -460px; +} +.emoji-flag_gf { + background-position: -260px -460px; +} +.emoji-flag_gg { + background-position: -280px -460px; +} +.emoji-flag_gh { + background-position: -300px -460px; +} +.emoji-flag_gi { + background-position: -320px -460px; +} +.emoji-flag_gl { + background-position: -340px -460px; +} +.emoji-flag_gm { + background-position: -360px -460px; +} +.emoji-flag_gn { + background-position: -380px -460px; +} +.emoji-flag_gp { + background-position: -400px -460px; +} +.emoji-flag_gq { + background-position: -420px -460px; +} +.emoji-flag_gr { + background-position: -440px -460px; +} +.emoji-flag_gs { + background-position: -460px -460px; +} +.emoji-flag_gt { + background-position: -480px 0; +} +.emoji-flag_gu { + background-position: -480px -20px; +} +.emoji-flag_gw { + background-position: -480px -40px; +} +.emoji-flag_gy { + background-position: -480px -60px; +} +.emoji-flag_hk { + background-position: -480px -80px; +} +.emoji-flag_hm { + background-position: -480px -100px; +} +.emoji-flag_hn { + background-position: -480px -120px; +} +.emoji-flag_hr { + background-position: -480px -140px; +} +.emoji-flag_ht { + background-position: -480px -160px; +} +.emoji-flag_hu { + background-position: -480px -180px; +} +.emoji-flag_ic { + background-position: -480px -200px; +} +.emoji-flag_id { + background-position: -480px -220px; +} +.emoji-flag_ie { + background-position: -480px -240px; +} +.emoji-flag_il { + background-position: -480px -260px; +} +.emoji-flag_im { + background-position: -480px -280px; +} +.emoji-flag_in { + background-position: -480px -300px; +} +.emoji-flag_io { + background-position: -480px -320px; +} +.emoji-flag_iq { + background-position: -480px -340px; +} +.emoji-flag_ir { + background-position: -480px -360px; +} +.emoji-flag_is { + background-position: -480px -380px; +} +.emoji-flag_it { + background-position: -480px -400px; +} +.emoji-flag_je { + background-position: -480px -420px; +} +.emoji-flag_jm { + background-position: -480px -440px; +} +.emoji-flag_jo { + background-position: -480px -460px; +} +.emoji-flag_jp { + background-position: 0 -480px; +} +.emoji-flag_ke { + background-position: -20px -480px; +} +.emoji-flag_kg { + background-position: -40px -480px; +} +.emoji-flag_kh { + background-position: -60px -480px; +} +.emoji-flag_ki { + background-position: -80px -480px; +} +.emoji-flag_km { + background-position: -100px -480px; +} +.emoji-flag_kn { + background-position: -120px -480px; +} +.emoji-flag_kp { + background-position: -140px -480px; +} +.emoji-flag_kr { + background-position: -160px -480px; +} +.emoji-flag_kw { + background-position: -180px -480px; +} +.emoji-flag_ky { + background-position: -200px -480px; +} +.emoji-flag_kz { + background-position: -220px -480px; +} +.emoji-flag_la { + background-position: -240px -480px; +} +.emoji-flag_lb { + background-position: -260px -480px; +} +.emoji-flag_lc { + background-position: -280px -480px; +} +.emoji-flag_li { + background-position: -300px -480px; +} +.emoji-flag_lk { + background-position: -320px -480px; +} +.emoji-flag_lr { + background-position: -340px -480px; +} +.emoji-flag_ls { + background-position: -360px -480px; +} +.emoji-flag_lt { + background-position: -380px -480px; +} +.emoji-flag_lu { + background-position: -400px -480px; +} +.emoji-flag_lv { + background-position: -420px -480px; +} +.emoji-flag_ly { + background-position: -440px -480px; +} +.emoji-flag_ma { + background-position: -460px -480px; +} +.emoji-flag_mc { + background-position: -480px -480px; +} +.emoji-flag_md { + background-position: -500px 0; +} +.emoji-flag_me { + background-position: -500px -20px; +} +.emoji-flag_mf { + background-position: -500px -40px; +} +.emoji-flag_mg { + background-position: -500px -60px; +} +.emoji-flag_mh { + background-position: -500px -80px; +} +.emoji-flag_mk { + background-position: -500px -100px; +} +.emoji-flag_ml { + background-position: -500px -120px; +} +.emoji-flag_mm { + background-position: -500px -140px; +} +.emoji-flag_mn { + background-position: -500px -160px; +} +.emoji-flag_mo { + background-position: -500px -180px; +} +.emoji-flag_mp { + background-position: -500px -200px; +} +.emoji-flag_mq { + background-position: -500px -220px; +} +.emoji-flag_mr { + background-position: -500px -240px; +} +.emoji-flag_ms { + background-position: -500px -260px; +} +.emoji-flag_mt { + background-position: -500px -280px; +} +.emoji-flag_mu { + background-position: -500px -300px; +} +.emoji-flag_mv { + background-position: -500px -320px; +} +.emoji-flag_mw { + background-position: -500px -340px; +} +.emoji-flag_mx { + background-position: -500px -360px; +} +.emoji-flag_my { + background-position: -500px -380px; +} +.emoji-flag_mz { + background-position: -500px -400px; +} +.emoji-flag_na { + background-position: -500px -420px; +} +.emoji-flag_nc { + background-position: -500px -440px; +} +.emoji-flag_ne { + background-position: -500px -460px; +} +.emoji-flag_nf { + background-position: -500px -480px; +} +.emoji-flag_ng { + background-position: 0 -500px; +} +.emoji-flag_ni { + background-position: -20px -500px; +} +.emoji-flag_nl { + background-position: -40px -500px; +} +.emoji-flag_no { + background-position: -60px -500px; +} +.emoji-flag_np { + background-position: -80px -500px; +} +.emoji-flag_nr { + background-position: -100px -500px; +} +.emoji-flag_nu { + background-position: -120px -500px; +} +.emoji-flag_nz { + background-position: -140px -500px; +} +.emoji-flag_om { + background-position: -160px -500px; +} +.emoji-flag_pa { + background-position: -180px -500px; +} +.emoji-flag_pe { + background-position: -200px -500px; +} +.emoji-flag_pf { + background-position: -220px -500px; +} +.emoji-flag_pg { + background-position: -240px -500px; +} +.emoji-flag_ph { + background-position: -260px -500px; +} +.emoji-flag_pk { + background-position: -280px -500px; +} +.emoji-flag_pl { + background-position: -300px -500px; +} +.emoji-flag_pm { + background-position: -320px -500px; +} +.emoji-flag_pn { + background-position: -340px -500px; +} +.emoji-flag_pr { + background-position: -360px -500px; +} +.emoji-flag_ps { + background-position: -380px -500px; +} +.emoji-flag_pt { + background-position: -400px -500px; +} +.emoji-flag_pw { + background-position: -420px -500px; +} +.emoji-flag_py { + background-position: -440px -500px; +} +.emoji-flag_qa { + background-position: -460px -500px; +} +.emoji-flag_re { + background-position: -480px -500px; +} +.emoji-flag_ro { + background-position: -500px -500px; +} +.emoji-flag_rs { + background-position: -520px 0; +} +.emoji-flag_ru { + background-position: -520px -20px; +} +.emoji-flag_rw { + background-position: -520px -40px; +} +.emoji-flag_sa { + background-position: -520px -60px; +} +.emoji-flag_sb { + background-position: -520px -80px; +} +.emoji-flag_sc { + background-position: -520px -100px; +} +.emoji-flag_sd { + background-position: -520px -120px; +} +.emoji-flag_se { + background-position: -520px -140px; +} +.emoji-flag_sg { + background-position: -520px -160px; +} +.emoji-flag_sh { + background-position: -520px -180px; +} +.emoji-flag_si { + background-position: -520px -200px; +} +.emoji-flag_sj { + background-position: -520px -220px; +} +.emoji-flag_sk { + background-position: -520px -240px; +} +.emoji-flag_sl { + background-position: -520px -260px; +} +.emoji-flag_sm { + background-position: -520px -280px; +} +.emoji-flag_sn { + background-position: -520px -300px; +} +.emoji-flag_so { + background-position: -520px -320px; +} +.emoji-flag_sr { + background-position: -520px -340px; +} +.emoji-flag_ss { + background-position: -520px -360px; +} +.emoji-flag_st { + background-position: -520px -380px; +} +.emoji-flag_sv { + background-position: -520px -400px; +} +.emoji-flag_sx { + background-position: -520px -420px; +} +.emoji-flag_sy { + background-position: -520px -440px; +} +.emoji-flag_sz { + background-position: -520px -460px; +} +.emoji-flag_ta { + background-position: -520px -480px; +} +.emoji-flag_tc { + background-position: -520px -500px; +} +.emoji-flag_td { + background-position: 0 -520px; +} +.emoji-flag_tf { + background-position: -20px -520px; +} +.emoji-flag_tg { + background-position: -40px -520px; +} +.emoji-flag_th { + background-position: -60px -520px; +} +.emoji-flag_tj { + background-position: -80px -520px; +} +.emoji-flag_tk { + background-position: -100px -520px; +} +.emoji-flag_tl { + background-position: -120px -520px; +} +.emoji-flag_tm { + background-position: -140px -520px; +} +.emoji-flag_tn { + background-position: -160px -520px; +} +.emoji-flag_to { + background-position: -180px -520px; +} +.emoji-flag_tr { + background-position: -200px -520px; +} +.emoji-flag_tt { + background-position: -220px -520px; +} +.emoji-flag_tv { + background-position: -240px -520px; +} +.emoji-flag_tw { + background-position: -260px -520px; +} +.emoji-flag_tz { + background-position: -280px -520px; +} +.emoji-flag_ua { + background-position: -300px -520px; +} +.emoji-flag_ug { + background-position: -320px -520px; +} +.emoji-flag_um { + background-position: -340px -520px; +} +.emoji-flag_us { + background-position: -360px -520px; +} +.emoji-flag_uy { + background-position: -380px -520px; +} +.emoji-flag_uz { + background-position: -400px -520px; +} +.emoji-flag_va { + background-position: -420px -520px; +} +.emoji-flag_vc { + background-position: -440px -520px; +} +.emoji-flag_ve { + background-position: -460px -520px; +} +.emoji-flag_vg { + background-position: -480px -520px; +} +.emoji-flag_vi { + background-position: -500px -520px; +} +.emoji-flag_vn { + background-position: -520px -520px; +} +.emoji-flag_vu { + background-position: -540px 0; +} +.emoji-flag_wf { + background-position: -540px -20px; +} +.emoji-flag_white { + background-position: -540px -40px; +} +.emoji-flag_ws { + background-position: -540px -60px; +} +.emoji-flag_xk { + background-position: -540px -80px; +} +.emoji-flag_ye { + background-position: -540px -100px; +} +.emoji-flag_yt { + background-position: -540px -120px; +} +.emoji-flag_za { + background-position: -540px -140px; +} +.emoji-flag_zm { + background-position: -540px -160px; +} +.emoji-flag_zw { + background-position: -540px -180px; +} +.emoji-flags { + background-position: -540px -200px; +} +.emoji-flashlight { + background-position: -540px -220px; +} +.emoji-fleur-de-lis { + background-position: -540px -240px; +} +.emoji-floppy_disk { + background-position: -540px -260px; +} +.emoji-flower_playing_cards { + background-position: -540px -280px; +} +.emoji-flushed { + background-position: -540px -300px; +} +.emoji-fog { + background-position: -540px -320px; +} +.emoji-foggy { + background-position: -540px -340px; +} +.emoji-football { + background-position: -540px -360px; +} +.emoji-footprints { + background-position: -540px -380px; +} +.emoji-fork_and_knife { + background-position: -540px -400px; +} +.emoji-fork_knife_plate { + background-position: -540px -420px; +} +.emoji-fountain { + background-position: -540px -440px; +} +.emoji-four { + background-position: -540px -460px; +} +.emoji-four_leaf_clover { + background-position: -540px -480px; +} +.emoji-fox { + background-position: -540px -500px; +} +.emoji-frame_photo { + background-position: -540px -520px; +} +.emoji-free { + background-position: 0 -540px; +} +.emoji-french_bread { + background-position: -20px -540px; +} +.emoji-fried_shrimp { + background-position: -40px -540px; +} +.emoji-fries { + background-position: -60px -540px; +} +.emoji-frog { + background-position: -80px -540px; +} +.emoji-frowning { + background-position: -100px -540px; +} +.emoji-frowning2 { + background-position: -120px -540px; +} +.emoji-fuelpump { + background-position: -140px -540px; +} +.emoji-full_moon { + background-position: -160px -540px; +} +.emoji-full_moon_with_face { + background-position: -180px -540px; +} +.emoji-game_die { + background-position: -200px -540px; +} +.emoji-gay_pride_flag { + background-position: -220px -540px; +} +.emoji-gear { + background-position: -240px -540px; +} +.emoji-gem { + background-position: -260px -540px; +} +.emoji-gemini { + background-position: -280px -540px; +} +.emoji-ghost { + background-position: -300px -540px; +} +.emoji-gift { + background-position: -320px -540px; +} +.emoji-gift_heart { + background-position: -340px -540px; +} +.emoji-girl { + background-position: -360px -540px; +} +.emoji-girl_tone1 { + background-position: -380px -540px; +} +.emoji-girl_tone2 { + background-position: -400px -540px; +} +.emoji-girl_tone3 { + background-position: -420px -540px; +} +.emoji-girl_tone4 { + background-position: -440px -540px; +} +.emoji-girl_tone5 { + background-position: -460px -540px; +} +.emoji-globe_with_meridians { + background-position: -480px -540px; +} +.emoji-goal { + background-position: -500px -540px; +} +.emoji-goat { + background-position: -520px -540px; +} +.emoji-golf { + background-position: -540px -540px; +} +.emoji-golfer { + background-position: -560px 0; +} +.emoji-gorilla { + background-position: -560px -20px; +} +.emoji-grapes { + background-position: -560px -40px; +} +.emoji-green_apple { + background-position: -560px -60px; +} +.emoji-green_book { + background-position: -560px -80px; +} +.emoji-green_heart { + background-position: -560px -100px; +} +.emoji-grey_exclamation { + background-position: -560px -120px; +} +.emoji-grey_question { + background-position: -560px -140px; +} +.emoji-grimacing { + background-position: -560px -160px; +} +.emoji-grin { + background-position: -560px -180px; +} +.emoji-grinning { + background-position: -560px -200px; +} +.emoji-guardsman { + background-position: -560px -220px; +} +.emoji-guardsman_tone1 { + background-position: -560px -240px; +} +.emoji-guardsman_tone2 { + background-position: -560px -260px; +} +.emoji-guardsman_tone3 { + background-position: -560px -280px; +} +.emoji-guardsman_tone4 { + background-position: -560px -300px; +} +.emoji-guardsman_tone5 { + background-position: -560px -320px; +} +.emoji-guitar { + background-position: -560px -340px; +} +.emoji-gun { + background-position: -560px -360px; +} +.emoji-haircut { + background-position: -560px -380px; +} +.emoji-haircut_tone1 { + background-position: -560px -400px; +} +.emoji-haircut_tone2 { + background-position: -560px -420px; +} +.emoji-haircut_tone3 { + background-position: -560px -440px; +} +.emoji-haircut_tone4 { + background-position: -560px -460px; +} +.emoji-haircut_tone5 { + background-position: -560px -480px; +} +.emoji-hamburger { + background-position: -560px -500px; +} +.emoji-hammer { + background-position: -560px -520px; +} +.emoji-hammer_pick { + background-position: -560px -540px; +} +.emoji-hamster { + background-position: 0 -560px; +} +.emoji-hand_splayed { + background-position: -20px -560px; +} +.emoji-hand_splayed_tone1 { + background-position: -40px -560px; +} +.emoji-hand_splayed_tone2 { + background-position: -60px -560px; +} +.emoji-hand_splayed_tone3 { + background-position: -80px -560px; +} +.emoji-hand_splayed_tone4 { + background-position: -100px -560px; +} +.emoji-hand_splayed_tone5 { + background-position: -120px -560px; +} +.emoji-handbag { + background-position: -140px -560px; +} +.emoji-handball { + background-position: -160px -560px; +} +.emoji-handball_tone1 { + background-position: -180px -560px; +} +.emoji-handball_tone2 { + background-position: -200px -560px; +} +.emoji-handball_tone3 { + background-position: -220px -560px; +} +.emoji-handball_tone4 { + background-position: -240px -560px; +} +.emoji-handball_tone5 { + background-position: -260px -560px; +} +.emoji-handshake { + background-position: -280px -560px; +} +.emoji-handshake_tone1 { + background-position: -300px -560px; +} +.emoji-handshake_tone2 { + background-position: -320px -560px; +} +.emoji-handshake_tone3 { + background-position: -340px -560px; +} +.emoji-handshake_tone4 { + background-position: -360px -560px; +} +.emoji-handshake_tone5 { + background-position: -380px -560px; +} +.emoji-hash { + background-position: -400px -560px; +} +.emoji-hatched_chick { + background-position: -420px -560px; +} +.emoji-hatching_chick { + background-position: -440px -560px; +} +.emoji-head_bandage { + background-position: -460px -560px; +} +.emoji-headphones { + background-position: -480px -560px; +} +.emoji-hear_no_evil { + background-position: -500px -560px; +} +.emoji-heart { + background-position: -520px -560px; +} +.emoji-heart_decoration { + background-position: -540px -560px; +} +.emoji-heart_exclamation { + background-position: -560px -560px; +} +.emoji-heart_eyes { + background-position: -580px 0; +} +.emoji-heart_eyes_cat { + background-position: -580px -20px; +} +.emoji-heartbeat { + background-position: -580px -40px; +} +.emoji-heartpulse { + background-position: -580px -60px; +} +.emoji-hearts { + background-position: -580px -80px; +} +.emoji-heavy_check_mark { + background-position: -580px -100px; +} +.emoji-heavy_division_sign { + background-position: -580px -120px; +} +.emoji-heavy_dollar_sign { + background-position: -580px -140px; +} +.emoji-heavy_minus_sign { + background-position: -580px -160px; +} +.emoji-heavy_multiplication_x { + background-position: -580px -180px; +} +.emoji-heavy_plus_sign { + background-position: -580px -200px; +} +.emoji-helicopter { + background-position: -580px -220px; +} +.emoji-helmet_with_cross { + background-position: -580px -240px; +} +.emoji-herb { + background-position: -580px -260px; +} +.emoji-hibiscus { + background-position: -580px -280px; +} +.emoji-high_brightness { + background-position: -580px -300px; +} +.emoji-high_heel { + background-position: -580px -320px; +} +.emoji-hockey { + background-position: -580px -340px; +} +.emoji-hole { + background-position: -580px -360px; +} +.emoji-homes { + background-position: -580px -380px; +} +.emoji-honey_pot { + background-position: -580px -400px; +} +.emoji-horse { + background-position: -580px -420px; +} +.emoji-horse_racing { + background-position: -580px -440px; +} +.emoji-horse_racing_tone1 { + background-position: -580px -460px; +} +.emoji-horse_racing_tone2 { + background-position: -580px -480px; +} +.emoji-horse_racing_tone3 { + background-position: -580px -500px; +} +.emoji-horse_racing_tone4 { + background-position: -580px -520px; +} +.emoji-horse_racing_tone5 { + background-position: -580px -540px; +} +.emoji-hospital { + background-position: -580px -560px; +} +.emoji-hot_pepper { + background-position: 0 -580px; +} +.emoji-hotdog { + background-position: -20px -580px; +} +.emoji-hotel { + background-position: -40px -580px; +} +.emoji-hotsprings { + background-position: -60px -580px; +} +.emoji-hourglass { + background-position: -80px -580px; +} +.emoji-hourglass_flowing_sand { + background-position: -100px -580px; +} +.emoji-house { + background-position: -120px -580px; +} +.emoji-house_abandoned { + background-position: -140px -580px; +} +.emoji-house_with_garden { + background-position: -160px -580px; +} +.emoji-hugging { + background-position: -180px -580px; +} +.emoji-hushed { + background-position: -200px -580px; +} +.emoji-ice_cream { + background-position: -220px -580px; +} +.emoji-ice_skate { + background-position: -240px -580px; +} +.emoji-icecream { + background-position: -260px -580px; +} +.emoji-id { + background-position: -280px -580px; +} +.emoji-ideograph_advantage { + background-position: -300px -580px; +} +.emoji-imp { + background-position: -320px -580px; +} +.emoji-inbox_tray { + background-position: -340px -580px; +} +.emoji-incoming_envelope { + background-position: -360px -580px; +} +.emoji-information_desk_person { + background-position: -380px -580px; +} +.emoji-information_desk_person_tone1 { + background-position: -400px -580px; +} +.emoji-information_desk_person_tone2 { + background-position: -420px -580px; +} +.emoji-information_desk_person_tone3 { + background-position: -440px -580px; +} +.emoji-information_desk_person_tone4 { + background-position: -460px -580px; +} +.emoji-information_desk_person_tone5 { + background-position: -480px -580px; +} +.emoji-information_source { + background-position: -500px -580px; +} +.emoji-innocent { + background-position: -520px -580px; +} +.emoji-interrobang { + background-position: -540px -580px; +} +.emoji-iphone { + background-position: -560px -580px; +} +.emoji-island { + background-position: -580px -580px; +} +.emoji-izakaya_lantern { + background-position: -600px 0; +} +.emoji-jack_o_lantern { + background-position: -600px -20px; +} +.emoji-japan { + background-position: -600px -40px; +} +.emoji-japanese_castle { + background-position: -600px -60px; +} +.emoji-japanese_goblin { + background-position: -600px -80px; +} +.emoji-japanese_ogre { + background-position: -600px -100px; +} +.emoji-jeans { + background-position: -600px -120px; +} +.emoji-joy { + background-position: -600px -140px; +} +.emoji-joy_cat { + background-position: -600px -160px; +} +.emoji-joystick { + background-position: -600px -180px; +} +.emoji-juggling { + background-position: -600px -200px; +} +.emoji-juggling_tone1 { + background-position: -600px -220px; +} +.emoji-juggling_tone2 { + background-position: -600px -240px; +} +.emoji-juggling_tone3 { + background-position: -600px -260px; +} +.emoji-juggling_tone4 { + background-position: -600px -280px; +} +.emoji-juggling_tone5 { + background-position: -600px -300px; +} +.emoji-kaaba { + background-position: -600px -320px; +} +.emoji-key { + background-position: -600px -340px; +} +.emoji-key2 { + background-position: -600px -360px; +} +.emoji-keyboard { + background-position: -600px -380px; +} +.emoji-kimono { + background-position: -600px -400px; +} +.emoji-kiss { + background-position: -600px -420px; +} +.emoji-kiss_mm { + background-position: -600px -440px; +} +.emoji-kiss_ww { + background-position: -600px -460px; +} +.emoji-kissing { + background-position: -600px -480px; +} +.emoji-kissing_cat { + background-position: -600px -500px; +} +.emoji-kissing_closed_eyes { + background-position: -600px -520px; +} +.emoji-kissing_heart { + background-position: -600px -540px; +} +.emoji-kissing_smiling_eyes { + background-position: -600px -560px; +} +.emoji-kiwi { + background-position: -600px -580px; +} +.emoji-knife { + background-position: 0 -600px; +} +.emoji-koala { + background-position: -20px -600px; +} +.emoji-koko { + background-position: -40px -600px; +} +.emoji-label { + background-position: -60px -600px; +} +.emoji-large_blue_circle { + background-position: -80px -600px; +} +.emoji-large_blue_diamond { + background-position: -100px -600px; +} +.emoji-large_orange_diamond { + background-position: -120px -600px; +} +.emoji-last_quarter_moon { + background-position: -140px -600px; +} +.emoji-last_quarter_moon_with_face { + background-position: -160px -600px; +} +.emoji-laughing { + background-position: -180px -600px; +} +.emoji-leaves { + background-position: -200px -600px; +} +.emoji-ledger { + background-position: -220px -600px; +} +.emoji-left_facing_fist { + background-position: -240px -600px; +} +.emoji-left_facing_fist_tone1 { + background-position: -260px -600px; +} +.emoji-left_facing_fist_tone2 { + background-position: -280px -600px; +} +.emoji-left_facing_fist_tone3 { + background-position: -300px -600px; +} +.emoji-left_facing_fist_tone4 { + background-position: -320px -600px; +} +.emoji-left_facing_fist_tone5 { + background-position: -340px -600px; +} +.emoji-left_luggage { + background-position: -360px -600px; +} +.emoji-left_right_arrow { + background-position: -380px -600px; +} +.emoji-leftwards_arrow_with_hook { + background-position: -400px -600px; +} +.emoji-lemon { + background-position: -420px -600px; +} +.emoji-leo { + background-position: -440px -600px; +} +.emoji-leopard { + background-position: -460px -600px; +} +.emoji-level_slider { + background-position: -480px -600px; +} +.emoji-levitate { + background-position: -500px -600px; +} +.emoji-libra { + background-position: -520px -600px; +} +.emoji-lifter { + background-position: -540px -600px; +} +.emoji-lifter_tone1 { + background-position: -560px -600px; +} +.emoji-lifter_tone2 { + background-position: -580px -600px; +} +.emoji-lifter_tone3 { + background-position: -600px -600px; +} +.emoji-lifter_tone4 { + background-position: -620px 0; +} +.emoji-lifter_tone5 { + background-position: -620px -20px; +} +.emoji-light_rail { + background-position: -620px -40px; +} +.emoji-link { + background-position: -620px -60px; +} +.emoji-lion_face { + background-position: -620px -80px; +} +.emoji-lips { + background-position: -620px -100px; +} +.emoji-lipstick { + background-position: -620px -120px; +} +.emoji-lizard { + background-position: -620px -140px; +} +.emoji-lock { + background-position: -620px -160px; +} +.emoji-lock_with_ink_pen { + background-position: -620px -180px; +} +.emoji-lollipop { + background-position: -620px -200px; +} +.emoji-loop { + background-position: -620px -220px; +} +.emoji-loud_sound { + background-position: -620px -240px; +} +.emoji-loudspeaker { + background-position: -620px -260px; +} +.emoji-love_hotel { + background-position: -620px -280px; +} +.emoji-love_letter { + background-position: -620px -300px; +} +.emoji-low_brightness { + background-position: -620px -320px; +} +.emoji-lying_face { + background-position: -620px -340px; +} +.emoji-m { + background-position: -620px -360px; +} +.emoji-mag { + background-position: -620px -380px; +} +.emoji-mag_right { + background-position: -620px -400px; +} +.emoji-mahjong { + background-position: -620px -420px; +} +.emoji-mailbox { + background-position: -620px -440px; +} +.emoji-mailbox_closed { + background-position: -620px -460px; +} +.emoji-mailbox_with_mail { + background-position: -620px -480px; +} +.emoji-mailbox_with_no_mail { + background-position: -620px -500px; +} +.emoji-man { + background-position: -620px -520px; +} +.emoji-man_dancing { + background-position: -620px -540px; +} +.emoji-man_dancing_tone1 { + background-position: -620px -560px; +} +.emoji-man_dancing_tone2 { + background-position: -620px -580px; +} +.emoji-man_dancing_tone3 { + background-position: -620px -600px; +} +.emoji-man_dancing_tone4 { + background-position: 0 -620px; +} +.emoji-man_dancing_tone5 { + background-position: -20px -620px; +} +.emoji-man_in_tuxedo { + background-position: -40px -620px; +} +.emoji-man_in_tuxedo_tone1 { + background-position: -60px -620px; +} +.emoji-man_in_tuxedo_tone2 { + background-position: -80px -620px; +} +.emoji-man_in_tuxedo_tone3 { + background-position: -100px -620px; +} +.emoji-man_in_tuxedo_tone4 { + background-position: -120px -620px; +} +.emoji-man_in_tuxedo_tone5 { + background-position: -140px -620px; +} +.emoji-man_tone1 { + background-position: -160px -620px; +} +.emoji-man_tone2 { + background-position: -180px -620px; +} +.emoji-man_tone3 { + background-position: -200px -620px; +} +.emoji-man_tone4 { + background-position: -220px -620px; +} +.emoji-man_tone5 { + background-position: -240px -620px; +} +.emoji-man_with_gua_pi_mao { + background-position: -260px -620px; +} +.emoji-man_with_gua_pi_mao_tone1 { + background-position: -280px -620px; +} +.emoji-man_with_gua_pi_mao_tone2 { + background-position: -300px -620px; +} +.emoji-man_with_gua_pi_mao_tone3 { + background-position: -320px -620px; +} +.emoji-man_with_gua_pi_mao_tone4 { + background-position: -340px -620px; +} +.emoji-man_with_gua_pi_mao_tone5 { + background-position: -360px -620px; +} +.emoji-man_with_turban { + background-position: -380px -620px; +} +.emoji-man_with_turban_tone1 { + background-position: -400px -620px; +} +.emoji-man_with_turban_tone2 { + background-position: -420px -620px; +} +.emoji-man_with_turban_tone3 { + background-position: -440px -620px; +} +.emoji-man_with_turban_tone4 { + background-position: -460px -620px; +} +.emoji-man_with_turban_tone5 { + background-position: -480px -620px; +} +.emoji-mans_shoe { + background-position: -500px -620px; +} +.emoji-map { + background-position: -520px -620px; +} +.emoji-maple_leaf { + background-position: -540px -620px; +} +.emoji-martial_arts_uniform { + background-position: -560px -620px; +} +.emoji-mask { + background-position: -580px -620px; +} +.emoji-massage { + background-position: -600px -620px; +} +.emoji-massage_tone1 { + background-position: -620px -620px; +} +.emoji-massage_tone2 { + background-position: -640px 0; +} +.emoji-massage_tone3 { + background-position: -640px -20px; +} +.emoji-massage_tone4 { + background-position: -640px -40px; +} +.emoji-massage_tone5 { + background-position: -640px -60px; +} +.emoji-meat_on_bone { + background-position: -640px -80px; +} +.emoji-medal { + background-position: -640px -100px; +} +.emoji-mega { + background-position: -640px -120px; +} +.emoji-melon { + background-position: -640px -140px; +} +.emoji-menorah { + background-position: -640px -160px; +} +.emoji-mens { + background-position: -640px -180px; +} +.emoji-metal { + background-position: -640px -200px; +} +.emoji-metal_tone1 { + background-position: -640px -220px; +} +.emoji-metal_tone2 { + background-position: -640px -240px; +} +.emoji-metal_tone3 { + background-position: -640px -260px; +} +.emoji-metal_tone4 { + background-position: -640px -280px; +} +.emoji-metal_tone5 { + background-position: -640px -300px; +} +.emoji-metro { + background-position: -640px -320px; +} +.emoji-microphone { + background-position: -640px -340px; +} +.emoji-microphone2 { + background-position: -640px -360px; +} +.emoji-microscope { + background-position: -640px -380px; +} +.emoji-middle_finger { + background-position: -640px -400px; +} +.emoji-middle_finger_tone1 { + background-position: -640px -420px; +} +.emoji-middle_finger_tone2 { + background-position: -640px -440px; +} +.emoji-middle_finger_tone3 { + background-position: -640px -460px; +} +.emoji-middle_finger_tone4 { + background-position: -640px -480px; +} +.emoji-middle_finger_tone5 { + background-position: -640px -500px; +} +.emoji-military_medal { + background-position: -640px -520px; +} +.emoji-milk { + background-position: -640px -540px; +} +.emoji-milky_way { + background-position: -640px -560px; +} +.emoji-minibus { + background-position: -640px -580px; +} +.emoji-minidisc { + background-position: -640px -600px; +} +.emoji-mobile_phone_off { + background-position: -640px -620px; +} +.emoji-money_mouth { + background-position: 0 -640px; +} +.emoji-money_with_wings { + background-position: -20px -640px; +} +.emoji-moneybag { + background-position: -40px -640px; +} +.emoji-monkey { + background-position: -60px -640px; +} +.emoji-monkey_face { + background-position: -80px -640px; +} +.emoji-monorail { + background-position: -100px -640px; +} +.emoji-mortar_board { + background-position: -120px -640px; +} +.emoji-mosque { + background-position: -140px -640px; +} +.emoji-motor_scooter { + background-position: -160px -640px; +} +.emoji-motorboat { + background-position: -180px -640px; +} +.emoji-motorcycle { + background-position: -200px -640px; +} +.emoji-motorway { + background-position: -220px -640px; +} +.emoji-mount_fuji { + background-position: -240px -640px; +} +.emoji-mountain { + background-position: -260px -640px; +} +.emoji-mountain_bicyclist { + background-position: -280px -640px; +} +.emoji-mountain_bicyclist_tone1 { + background-position: -300px -640px; +} +.emoji-mountain_bicyclist_tone2 { + background-position: -320px -640px; +} +.emoji-mountain_bicyclist_tone3 { + background-position: -340px -640px; +} +.emoji-mountain_bicyclist_tone4 { + background-position: -360px -640px; +} +.emoji-mountain_bicyclist_tone5 { + background-position: -380px -640px; +} +.emoji-mountain_cableway { + background-position: -400px -640px; +} +.emoji-mountain_railway { + background-position: -420px -640px; +} +.emoji-mountain_snow { + background-position: -440px -640px; +} +.emoji-mouse { + background-position: -460px -640px; +} +.emoji-mouse2 { + background-position: -480px -640px; +} +.emoji-mouse_three_button { + background-position: -500px -640px; +} +.emoji-movie_camera { + background-position: -520px -640px; +} +.emoji-moyai { + background-position: -540px -640px; +} +.emoji-mrs_claus { + background-position: -560px -640px; +} +.emoji-mrs_claus_tone1 { + background-position: -580px -640px; +} +.emoji-mrs_claus_tone2 { + background-position: -600px -640px; +} +.emoji-mrs_claus_tone3 { + background-position: -620px -640px; +} +.emoji-mrs_claus_tone4 { + background-position: -640px -640px; +} +.emoji-mrs_claus_tone5 { + background-position: -660px 0; +} +.emoji-muscle { + background-position: -660px -20px; +} +.emoji-muscle_tone1 { + background-position: -660px -40px; +} +.emoji-muscle_tone2 { + background-position: -660px -60px; +} +.emoji-muscle_tone3 { + background-position: -660px -80px; +} +.emoji-muscle_tone4 { + background-position: -660px -100px; +} +.emoji-muscle_tone5 { + background-position: -660px -120px; +} +.emoji-mushroom { + background-position: -660px -140px; +} +.emoji-musical_keyboard { + background-position: -660px -160px; +} +.emoji-musical_note { + background-position: -660px -180px; +} +.emoji-musical_score { + background-position: -660px -200px; +} +.emoji-mute { + background-position: -660px -220px; +} +.emoji-nail_care { + background-position: -660px -240px; +} +.emoji-nail_care_tone1 { + background-position: -660px -260px; +} +.emoji-nail_care_tone2 { + background-position: -660px -280px; +} +.emoji-nail_care_tone3 { + background-position: -660px -300px; +} +.emoji-nail_care_tone4 { + background-position: -660px -320px; +} +.emoji-nail_care_tone5 { + background-position: -660px -340px; +} +.emoji-name_badge { + background-position: -660px -360px; +} +.emoji-nauseated_face { + background-position: -660px -380px; +} +.emoji-necktie { + background-position: -660px -400px; +} +.emoji-negative_squared_cross_mark { + background-position: -660px -420px; +} +.emoji-nerd { + background-position: -660px -440px; +} +.emoji-neutral_face { + background-position: -660px -460px; +} +.emoji-new { + background-position: -660px -480px; +} +.emoji-new_moon { + background-position: -660px -500px; +} +.emoji-new_moon_with_face { + background-position: -660px -520px; +} +.emoji-newspaper { + background-position: -660px -540px; +} +.emoji-newspaper2 { + background-position: -660px -560px; +} +.emoji-ng { + background-position: -660px -580px; +} +.emoji-night_with_stars { + background-position: -660px -600px; +} +.emoji-nine { + background-position: -660px -620px; +} +.emoji-no_bell { + background-position: -660px -640px; +} +.emoji-no_bicycles { + background-position: 0 -660px; +} +.emoji-no_entry { + background-position: -20px -660px; +} +.emoji-no_entry_sign { + background-position: -40px -660px; +} +.emoji-no_good { + background-position: -60px -660px; +} +.emoji-no_good_tone1 { + background-position: -80px -660px; +} +.emoji-no_good_tone2 { + background-position: -100px -660px; +} +.emoji-no_good_tone3 { + background-position: -120px -660px; +} +.emoji-no_good_tone4 { + background-position: -140px -660px; +} +.emoji-no_good_tone5 { + background-position: -160px -660px; +} +.emoji-no_mobile_phones { + background-position: -180px -660px; +} +.emoji-no_mouth { + background-position: -200px -660px; +} +.emoji-no_pedestrians { + background-position: -220px -660px; +} +.emoji-no_smoking { + background-position: -240px -660px; +} +.emoji-non-potable_water { + background-position: -260px -660px; +} +.emoji-nose { + background-position: -280px -660px; +} +.emoji-nose_tone1 { + background-position: -300px -660px; +} +.emoji-nose_tone2 { + background-position: -320px -660px; +} +.emoji-nose_tone3 { + background-position: -340px -660px; +} +.emoji-nose_tone4 { + background-position: -360px -660px; +} +.emoji-nose_tone5 { + background-position: -380px -660px; +} +.emoji-notebook { + background-position: -400px -660px; +} +.emoji-notebook_with_decorative_cover { + background-position: -420px -660px; +} +.emoji-notepad_spiral { + background-position: -440px -660px; +} +.emoji-notes { + background-position: -460px -660px; +} +.emoji-nut_and_bolt { + background-position: -480px -660px; +} +.emoji-o { + background-position: -500px -660px; +} +.emoji-o2 { + background-position: -520px -660px; +} +.emoji-ocean { + background-position: -540px -660px; +} +.emoji-octagonal_sign { + background-position: -560px -660px; +} +.emoji-octopus { + background-position: -580px -660px; +} +.emoji-oden { + background-position: -600px -660px; +} +.emoji-office { + background-position: -620px -660px; +} +.emoji-oil { + background-position: -640px -660px; +} +.emoji-ok { + background-position: -660px -660px; +} +.emoji-ok_hand { + background-position: -680px 0; +} +.emoji-ok_hand_tone1 { + background-position: -680px -20px; +} +.emoji-ok_hand_tone2 { + background-position: -680px -40px; +} +.emoji-ok_hand_tone3 { + background-position: -680px -60px; +} +.emoji-ok_hand_tone4 { + background-position: -680px -80px; +} +.emoji-ok_hand_tone5 { + background-position: -680px -100px; +} +.emoji-ok_woman { + background-position: -680px -120px; +} +.emoji-ok_woman_tone1 { + background-position: -680px -140px; +} +.emoji-ok_woman_tone2 { + background-position: -680px -160px; +} +.emoji-ok_woman_tone3 { + background-position: -680px -180px; +} +.emoji-ok_woman_tone4 { + background-position: -680px -200px; +} +.emoji-ok_woman_tone5 { + background-position: -680px -220px; +} +.emoji-older_man { + background-position: -680px -240px; +} +.emoji-older_man_tone1 { + background-position: -680px -260px; +} +.emoji-older_man_tone2 { + background-position: -680px -280px; +} +.emoji-older_man_tone3 { + background-position: -680px -300px; +} +.emoji-older_man_tone4 { + background-position: -680px -320px; +} +.emoji-older_man_tone5 { + background-position: -680px -340px; +} +.emoji-older_woman { + background-position: -680px -360px; +} +.emoji-older_woman_tone1 { + background-position: -680px -380px; +} +.emoji-older_woman_tone2 { + background-position: -680px -400px; +} +.emoji-older_woman_tone3 { + background-position: -680px -420px; +} +.emoji-older_woman_tone4 { + background-position: -680px -440px; +} +.emoji-older_woman_tone5 { + background-position: -680px -460px; +} +.emoji-om_symbol { + background-position: -680px -480px; +} +.emoji-on { + background-position: -680px -500px; +} +.emoji-oncoming_automobile { + background-position: -680px -520px; +} +.emoji-oncoming_bus { + background-position: -680px -540px; +} +.emoji-oncoming_police_car { + background-position: -680px -560px; +} +.emoji-oncoming_taxi { + background-position: -680px -580px; +} +.emoji-one { + background-position: -680px -600px; +} +.emoji-open_file_folder { + background-position: -680px -620px; +} +.emoji-open_hands { + background-position: -680px -640px; +} +.emoji-open_hands_tone1 { + background-position: -680px -660px; +} +.emoji-open_hands_tone2 { + background-position: 0 -680px; +} +.emoji-open_hands_tone3 { + background-position: -20px -680px; +} +.emoji-open_hands_tone4 { + background-position: -40px -680px; +} +.emoji-open_hands_tone5 { + background-position: -60px -680px; +} +.emoji-open_mouth { + background-position: -80px -680px; +} +.emoji-ophiuchus { + background-position: -100px -680px; +} +.emoji-orange_book { + background-position: -120px -680px; +} +.emoji-orthodox_cross { + background-position: -140px -680px; +} +.emoji-outbox_tray { + background-position: -160px -680px; +} +.emoji-owl { + background-position: -180px -680px; +} +.emoji-ox { + background-position: -200px -680px; +} +.emoji-package { + background-position: -220px -680px; +} +.emoji-page_facing_up { + background-position: -240px -680px; +} +.emoji-page_with_curl { + background-position: -260px -680px; +} +.emoji-pager { + background-position: -280px -680px; +} +.emoji-paintbrush { + background-position: -300px -680px; +} +.emoji-palm_tree { + background-position: -320px -680px; +} +.emoji-pancakes { + background-position: -340px -680px; +} +.emoji-panda_face { + background-position: -360px -680px; +} +.emoji-paperclip { + background-position: -380px -680px; +} +.emoji-paperclips { + background-position: -400px -680px; +} +.emoji-park { + background-position: -420px -680px; +} +.emoji-parking { + background-position: -440px -680px; +} +.emoji-part_alternation_mark { + background-position: -460px -680px; +} +.emoji-partly_sunny { + background-position: -480px -680px; +} +.emoji-passport_control { + background-position: -500px -680px; +} +.emoji-pause_button { + background-position: -520px -680px; +} +.emoji-peace { + background-position: -540px -680px; +} +.emoji-peach { + background-position: -560px -680px; +} +.emoji-peanuts { + background-position: -580px -680px; +} +.emoji-pear { + background-position: -600px -680px; +} +.emoji-pen_ballpoint { + background-position: -620px -680px; +} +.emoji-pen_fountain { + background-position: -640px -680px; +} +.emoji-pencil { + background-position: -660px -680px; +} +.emoji-pencil2 { + background-position: -680px -680px; +} +.emoji-penguin { + background-position: -700px 0; +} +.emoji-pensive { + background-position: -700px -20px; +} +.emoji-performing_arts { + background-position: -700px -40px; +} +.emoji-persevere { + background-position: -700px -60px; +} +.emoji-person_frowning { + background-position: -700px -80px; +} +.emoji-person_frowning_tone1 { + background-position: -700px -100px; +} +.emoji-person_frowning_tone2 { + background-position: -700px -120px; +} +.emoji-person_frowning_tone3 { + background-position: -700px -140px; +} +.emoji-person_frowning_tone4 { + background-position: -700px -160px; +} +.emoji-person_frowning_tone5 { + background-position: -700px -180px; +} +.emoji-person_with_blond_hair { + background-position: -700px -200px; +} +.emoji-person_with_blond_hair_tone1 { + background-position: -700px -220px; +} +.emoji-person_with_blond_hair_tone2 { + background-position: -700px -240px; +} +.emoji-person_with_blond_hair_tone3 { + background-position: -700px -260px; +} +.emoji-person_with_blond_hair_tone4 { + background-position: -700px -280px; +} +.emoji-person_with_blond_hair_tone5 { + background-position: -700px -300px; +} +.emoji-person_with_pouting_face { + background-position: -700px -320px; +} +.emoji-person_with_pouting_face_tone1 { + background-position: -700px -340px; +} +.emoji-person_with_pouting_face_tone2 { + background-position: -700px -360px; +} +.emoji-person_with_pouting_face_tone3 { + background-position: -700px -380px; +} +.emoji-person_with_pouting_face_tone4 { + background-position: -700px -400px; +} +.emoji-person_with_pouting_face_tone5 { + background-position: -700px -420px; +} +.emoji-pick { + background-position: -700px -440px; +} +.emoji-pig { + background-position: -700px -460px; +} +.emoji-pig2 { + background-position: -700px -480px; +} +.emoji-pig_nose { + background-position: -700px -500px; +} +.emoji-pill { + background-position: -700px -520px; +} +.emoji-pineapple { + background-position: -700px -540px; +} +.emoji-ping_pong { + background-position: -700px -560px; +} +.emoji-pisces { + background-position: -700px -580px; +} +.emoji-pizza { + background-position: -700px -600px; +} +.emoji-place_of_worship { + background-position: -700px -620px; +} +.emoji-play_pause { + background-position: -700px -640px; +} +.emoji-point_down { + background-position: -700px -660px; +} +.emoji-point_down_tone1 { + background-position: -700px -680px; +} +.emoji-point_down_tone2 { + background-position: 0 -700px; +} +.emoji-point_down_tone3 { + background-position: -20px -700px; +} +.emoji-point_down_tone4 { + background-position: -40px -700px; +} +.emoji-point_down_tone5 { + background-position: -60px -700px; +} +.emoji-point_left { + background-position: -80px -700px; +} +.emoji-point_left_tone1 { + background-position: -100px -700px; +} +.emoji-point_left_tone2 { + background-position: -120px -700px; +} +.emoji-point_left_tone3 { + background-position: -140px -700px; +} +.emoji-point_left_tone4 { + background-position: -160px -700px; +} +.emoji-point_left_tone5 { + background-position: -180px -700px; +} +.emoji-point_right { + background-position: -200px -700px; +} +.emoji-point_right_tone1 { + background-position: -220px -700px; +} +.emoji-point_right_tone2 { + background-position: -240px -700px; +} +.emoji-point_right_tone3 { + background-position: -260px -700px; +} +.emoji-point_right_tone4 { + background-position: -280px -700px; +} +.emoji-point_right_tone5 { + background-position: -300px -700px; +} +.emoji-point_up { + background-position: -320px -700px; +} +.emoji-point_up_2 { + background-position: -340px -700px; +} +.emoji-point_up_2_tone1 { + background-position: -360px -700px; +} +.emoji-point_up_2_tone2 { + background-position: -380px -700px; +} +.emoji-point_up_2_tone3 { + background-position: -400px -700px; +} +.emoji-point_up_2_tone4 { + background-position: -420px -700px; +} +.emoji-point_up_2_tone5 { + background-position: -440px -700px; +} +.emoji-point_up_tone1 { + background-position: -460px -700px; +} +.emoji-point_up_tone2 { + background-position: -480px -700px; +} +.emoji-point_up_tone3 { + background-position: -500px -700px; +} +.emoji-point_up_tone4 { + background-position: -520px -700px; +} +.emoji-point_up_tone5 { + background-position: -540px -700px; +} +.emoji-police_car { + background-position: -560px -700px; +} +.emoji-poodle { + background-position: -580px -700px; +} +.emoji-poop { + background-position: -600px -700px; +} +.emoji-popcorn { + background-position: -620px -700px; +} +.emoji-post_office { + background-position: -640px -700px; +} +.emoji-postal_horn { + background-position: -660px -700px; +} +.emoji-postbox { + background-position: -680px -700px; +} +.emoji-potable_water { + background-position: -700px -700px; +} +.emoji-potato { + background-position: -720px 0; +} +.emoji-pouch { + background-position: -720px -20px; +} +.emoji-poultry_leg { + background-position: -720px -40px; +} +.emoji-pound { + background-position: -720px -60px; +} +.emoji-pouting_cat { + background-position: -720px -80px; +} +.emoji-pray { + background-position: -720px -100px; +} +.emoji-pray_tone1 { + background-position: -720px -120px; +} +.emoji-pray_tone2 { + background-position: -720px -140px; +} +.emoji-pray_tone3 { + background-position: -720px -160px; +} +.emoji-pray_tone4 { + background-position: -720px -180px; +} +.emoji-pray_tone5 { + background-position: -720px -200px; +} +.emoji-prayer_beads { + background-position: -720px -220px; +} +.emoji-pregnant_woman { + background-position: -720px -240px; +} +.emoji-pregnant_woman_tone1 { + background-position: -720px -260px; +} +.emoji-pregnant_woman_tone2 { + background-position: -720px -280px; +} +.emoji-pregnant_woman_tone3 { + background-position: -720px -300px; +} +.emoji-pregnant_woman_tone4 { + background-position: -720px -320px; +} +.emoji-pregnant_woman_tone5 { + background-position: -720px -340px; +} +.emoji-prince { + background-position: -720px -360px; +} +.emoji-prince_tone1 { + background-position: -720px -380px; +} +.emoji-prince_tone2 { + background-position: -720px -400px; +} +.emoji-prince_tone3 { + background-position: -720px -420px; +} +.emoji-prince_tone4 { + background-position: -720px -440px; +} +.emoji-prince_tone5 { + background-position: -720px -460px; +} +.emoji-princess { + background-position: -720px -480px; +} +.emoji-princess_tone1 { + background-position: -720px -500px; +} +.emoji-princess_tone2 { + background-position: -720px -520px; +} +.emoji-princess_tone3 { + background-position: -720px -540px; +} +.emoji-princess_tone4 { + background-position: -720px -560px; +} +.emoji-princess_tone5 { + background-position: -720px -580px; +} +.emoji-printer { + background-position: -720px -600px; +} +.emoji-projector { + background-position: -720px -620px; +} +.emoji-punch { + background-position: -720px -640px; +} +.emoji-punch_tone1 { + background-position: -720px -660px; +} +.emoji-punch_tone2 { + background-position: -720px -680px; +} +.emoji-punch_tone3 { + background-position: -720px -700px; +} +.emoji-punch_tone4 { + background-position: 0 -720px; +} +.emoji-punch_tone5 { + background-position: -20px -720px; +} +.emoji-purple_heart { + background-position: -40px -720px; +} +.emoji-purse { + background-position: -60px -720px; +} +.emoji-pushpin { + background-position: -80px -720px; +} +.emoji-put_litter_in_its_place { + background-position: -100px -720px; +} +.emoji-question { + background-position: -120px -720px; +} +.emoji-rabbit { + background-position: -140px -720px; +} +.emoji-rabbit2 { + background-position: -160px -720px; +} +.emoji-race_car { + background-position: -180px -720px; +} +.emoji-racehorse { + background-position: -200px -720px; +} +.emoji-radio { + background-position: -220px -720px; +} +.emoji-radio_button { + background-position: -240px -720px; +} +.emoji-radioactive { + background-position: -260px -720px; +} +.emoji-rage { + background-position: -280px -720px; +} +.emoji-railway_car { + background-position: -300px -720px; +} +.emoji-railway_track { + background-position: -320px -720px; +} +.emoji-rainbow { + background-position: -340px -720px; +} +.emoji-raised_back_of_hand { + background-position: -360px -720px; +} +.emoji-raised_back_of_hand_tone1 { + background-position: -380px -720px; +} +.emoji-raised_back_of_hand_tone2 { + background-position: -400px -720px; +} +.emoji-raised_back_of_hand_tone3 { + background-position: -420px -720px; +} +.emoji-raised_back_of_hand_tone4 { + background-position: -440px -720px; +} +.emoji-raised_back_of_hand_tone5 { + background-position: -460px -720px; +} +.emoji-raised_hand { + background-position: -480px -720px; +} +.emoji-raised_hand_tone1 { + background-position: -500px -720px; +} +.emoji-raised_hand_tone2 { + background-position: -520px -720px; +} +.emoji-raised_hand_tone3 { + background-position: -540px -720px; +} +.emoji-raised_hand_tone4 { + background-position: -560px -720px; +} +.emoji-raised_hand_tone5 { + background-position: -580px -720px; +} +.emoji-raised_hands { + background-position: -600px -720px; +} +.emoji-raised_hands_tone1 { + background-position: -620px -720px; +} +.emoji-raised_hands_tone2 { + background-position: -640px -720px; +} +.emoji-raised_hands_tone3 { + background-position: -660px -720px; +} +.emoji-raised_hands_tone4 { + background-position: -680px -720px; +} +.emoji-raised_hands_tone5 { + background-position: -700px -720px; +} +.emoji-raising_hand { + background-position: -720px -720px; +} +.emoji-raising_hand_tone1 { + background-position: -740px 0; +} +.emoji-raising_hand_tone2 { + background-position: -740px -20px; +} +.emoji-raising_hand_tone3 { + background-position: -740px -40px; +} +.emoji-raising_hand_tone4 { + background-position: -740px -60px; +} +.emoji-raising_hand_tone5 { + background-position: -740px -80px; +} +.emoji-ram { + background-position: -740px -100px; +} +.emoji-ramen { + background-position: -740px -120px; +} +.emoji-rat { + background-position: -740px -140px; +} +.emoji-record_button { + background-position: -740px -160px; +} +.emoji-recycle { + background-position: -740px -180px; +} +.emoji-red_car { + background-position: -740px -200px; +} +.emoji-red_circle { + background-position: -740px -220px; +} +.emoji-registered { + background-position: -740px -240px; +} +.emoji-relaxed { + background-position: -740px -260px; +} +.emoji-relieved { + background-position: -740px -280px; +} +.emoji-reminder_ribbon { + background-position: -740px -300px; +} +.emoji-repeat { + background-position: -740px -320px; +} +.emoji-repeat_one { + background-position: -740px -340px; +} +.emoji-restroom { + background-position: -740px -360px; +} +.emoji-revolving_hearts { + background-position: -740px -380px; +} +.emoji-rewind { + background-position: -740px -400px; +} +.emoji-rhino { + background-position: -740px -420px; +} +.emoji-ribbon { + background-position: -740px -440px; +} +.emoji-rice { + background-position: -740px -460px; +} +.emoji-rice_ball { + background-position: -740px -480px; +} +.emoji-rice_cracker { + background-position: -740px -500px; +} +.emoji-rice_scene { + background-position: -740px -520px; +} +.emoji-right_facing_fist { + background-position: -740px -540px; +} +.emoji-right_facing_fist_tone1 { + background-position: -740px -560px; +} +.emoji-right_facing_fist_tone2 { + background-position: -740px -580px; +} +.emoji-right_facing_fist_tone3 { + background-position: -740px -600px; +} +.emoji-right_facing_fist_tone4 { + background-position: -740px -620px; +} +.emoji-right_facing_fist_tone5 { + background-position: -740px -640px; +} +.emoji-ring { + background-position: -740px -660px; +} +.emoji-robot { + background-position: -740px -680px; +} +.emoji-rocket { + background-position: -740px -700px; +} +.emoji-rofl { + background-position: -740px -720px; +} +.emoji-roller_coaster { + background-position: 0 -740px; +} +.emoji-rolling_eyes { + background-position: -20px -740px; +} +.emoji-rooster { + background-position: -40px -740px; +} +.emoji-rose { + background-position: -60px -740px; +} +.emoji-rosette { + background-position: -80px -740px; +} +.emoji-rotating_light { + background-position: -100px -740px; +} +.emoji-round_pushpin { + background-position: -120px -740px; +} +.emoji-rowboat { + background-position: -140px -740px; +} +.emoji-rowboat_tone1 { + background-position: -160px -740px; +} +.emoji-rowboat_tone2 { + background-position: -180px -740px; +} +.emoji-rowboat_tone3 { + background-position: -200px -740px; +} +.emoji-rowboat_tone4 { + background-position: -220px -740px; +} +.emoji-rowboat_tone5 { + background-position: -240px -740px; +} +.emoji-rugby_football { + background-position: -260px -740px; +} +.emoji-runner { + background-position: -280px -740px; +} +.emoji-runner_tone1 { + background-position: -300px -740px; +} +.emoji-runner_tone2 { + background-position: -320px -740px; +} +.emoji-runner_tone3 { + background-position: -340px -740px; +} +.emoji-runner_tone4 { + background-position: -360px -740px; +} +.emoji-runner_tone5 { + background-position: -380px -740px; +} +.emoji-running_shirt_with_sash { + background-position: -400px -740px; +} +.emoji-sa { + background-position: -420px -740px; +} +.emoji-sagittarius { + background-position: -440px -740px; +} +.emoji-sailboat { + background-position: -460px -740px; +} +.emoji-sake { + background-position: -480px -740px; +} +.emoji-salad { + background-position: -500px -740px; +} +.emoji-sandal { + background-position: -520px -740px; +} +.emoji-santa { + background-position: -540px -740px; +} +.emoji-santa_tone1 { + background-position: -560px -740px; +} +.emoji-santa_tone2 { + background-position: -580px -740px; +} +.emoji-santa_tone3 { + background-position: -600px -740px; +} +.emoji-santa_tone4 { + background-position: -620px -740px; +} +.emoji-santa_tone5 { + background-position: -640px -740px; +} +.emoji-satellite { + background-position: -660px -740px; +} +.emoji-satellite_orbital { + background-position: -680px -740px; +} +.emoji-saxophone { + background-position: -700px -740px; +} +.emoji-scales { + background-position: -720px -740px; +} +.emoji-school { + background-position: -740px -740px; +} +.emoji-school_satchel { + background-position: -760px 0; +} +.emoji-scissors { + background-position: -760px -20px; +} +.emoji-scooter { + background-position: -760px -40px; +} +.emoji-scorpion { + background-position: -760px -60px; +} +.emoji-scorpius { + background-position: -760px -80px; +} +.emoji-scream { + background-position: -760px -100px; +} +.emoji-scream_cat { + background-position: -760px -120px; +} +.emoji-scroll { + background-position: -760px -140px; +} +.emoji-seat { + background-position: -760px -160px; +} +.emoji-second_place { + background-position: -760px -180px; +} +.emoji-secret { + background-position: -760px -200px; +} +.emoji-see_no_evil { + background-position: -760px -220px; +} +.emoji-seedling { + background-position: -760px -240px; +} +.emoji-selfie { + background-position: -760px -260px; +} +.emoji-selfie_tone1 { + background-position: -760px -280px; +} +.emoji-selfie_tone2 { + background-position: -760px -300px; +} +.emoji-selfie_tone3 { + background-position: -760px -320px; +} +.emoji-selfie_tone4 { + background-position: -760px -340px; +} +.emoji-selfie_tone5 { + background-position: -760px -360px; +} +.emoji-seven { + background-position: -760px -380px; +} +.emoji-shallow_pan_of_food { + background-position: -760px -400px; +} +.emoji-shamrock { + background-position: -760px -420px; +} +.emoji-shark { + background-position: -760px -440px; +} +.emoji-shaved_ice { + background-position: -760px -460px; +} +.emoji-sheep { + background-position: -760px -480px; +} +.emoji-shell { + background-position: -760px -500px; +} +.emoji-shield { + background-position: -760px -520px; +} +.emoji-shinto_shrine { + background-position: -760px -540px; +} +.emoji-ship { + background-position: -760px -560px; +} +.emoji-shirt { + background-position: -760px -580px; +} +.emoji-shopping_bags { + background-position: -760px -600px; +} +.emoji-shopping_cart { + background-position: -760px -620px; +} +.emoji-shower { + background-position: -760px -640px; +} +.emoji-shrimp { + background-position: -760px -660px; +} +.emoji-shrug { + background-position: -760px -680px; +} +.emoji-shrug_tone1 { + background-position: -760px -700px; +} +.emoji-shrug_tone2 { + background-position: -760px -720px; +} +.emoji-shrug_tone3 { + background-position: -760px -740px; +} +.emoji-shrug_tone4 { + background-position: 0 -760px; +} +.emoji-shrug_tone5 { + background-position: -20px -760px; +} +.emoji-signal_strength { + background-position: -40px -760px; +} +.emoji-six { + background-position: -60px -760px; +} +.emoji-six_pointed_star { + background-position: -80px -760px; +} +.emoji-ski { + background-position: -100px -760px; +} +.emoji-skier { + background-position: -120px -760px; +} +.emoji-skull { + background-position: -140px -760px; +} +.emoji-skull_crossbones { + background-position: -160px -760px; +} +.emoji-sleeping { + background-position: -180px -760px; +} +.emoji-sleeping_accommodation { + background-position: -200px -760px; +} +.emoji-sleepy { + background-position: -220px -760px; +} +.emoji-slight_frown { + background-position: -240px -760px; +} +.emoji-slight_smile { + background-position: -260px -760px; +} +.emoji-slot_machine { + background-position: -280px -760px; +} +.emoji-small_blue_diamond { + background-position: -300px -760px; +} +.emoji-small_orange_diamond { + background-position: -320px -760px; +} +.emoji-small_red_triangle { + background-position: -340px -760px; +} +.emoji-small_red_triangle_down { + background-position: -360px -760px; +} +.emoji-smile { + background-position: -380px -760px; +} +.emoji-smile_cat { + background-position: -400px -760px; +} +.emoji-smiley { + background-position: -420px -760px; +} +.emoji-smiley_cat { + background-position: -440px -760px; +} +.emoji-smiling_imp { + background-position: -460px -760px; +} +.emoji-smirk { + background-position: -480px -760px; +} +.emoji-smirk_cat { + background-position: -500px -760px; +} +.emoji-smoking { + background-position: -520px -760px; +} +.emoji-snail { + background-position: -540px -760px; +} +.emoji-snake { + background-position: -560px -760px; +} +.emoji-sneezing_face { + background-position: -580px -760px; +} +.emoji-snowboarder { + background-position: -600px -760px; +} +.emoji-snowflake { + background-position: -620px -760px; +} +.emoji-snowman { + background-position: -640px -760px; +} +.emoji-snowman2 { + background-position: -660px -760px; +} +.emoji-sob { + background-position: -680px -760px; +} +.emoji-soccer { + background-position: -700px -760px; +} +.emoji-soon { + background-position: -720px -760px; +} +.emoji-sos { + background-position: -740px -760px; +} +.emoji-sound { + background-position: -760px -760px; +} +.emoji-space_invader { + background-position: -780px 0; +} +.emoji-spades { + background-position: -780px -20px; +} +.emoji-spaghetti { + background-position: -780px -40px; +} +.emoji-sparkle { + background-position: -780px -60px; +} +.emoji-sparkler { + background-position: -780px -80px; +} +.emoji-sparkles { + background-position: -780px -100px; +} +.emoji-sparkling_heart { + background-position: -780px -120px; +} +.emoji-speak_no_evil { + background-position: -780px -140px; +} +.emoji-speaker { + background-position: -780px -160px; +} +.emoji-speaking_head { + background-position: -780px -180px; +} +.emoji-speech_balloon { + background-position: -780px -200px; +} +.emoji-speech_left { + background-position: -780px -220px; +} +.emoji-speedboat { + background-position: -780px -240px; +} +.emoji-spider { + background-position: -780px -260px; +} +.emoji-spider_web { + background-position: -780px -280px; +} +.emoji-spoon { + background-position: -780px -300px; +} +.emoji-spy { + background-position: -780px -320px; +} +.emoji-spy_tone1 { + background-position: -780px -340px; +} +.emoji-spy_tone2 { + background-position: -780px -360px; +} +.emoji-spy_tone3 { + background-position: -780px -380px; +} +.emoji-spy_tone4 { + background-position: -780px -400px; +} +.emoji-spy_tone5 { + background-position: -780px -420px; +} +.emoji-squid { + background-position: -780px -440px; +} +.emoji-stadium { + background-position: -780px -460px; +} +.emoji-star { + background-position: -780px -480px; +} +.emoji-star2 { + background-position: -780px -500px; +} +.emoji-star_and_crescent { + background-position: -780px -520px; +} +.emoji-star_of_david { + background-position: -780px -540px; +} +.emoji-stars { + background-position: -780px -560px; +} +.emoji-station { + background-position: -780px -580px; +} +.emoji-statue_of_liberty { + background-position: -780px -600px; +} +.emoji-steam_locomotive { + background-position: -780px -620px; +} +.emoji-stew { + background-position: -780px -640px; +} +.emoji-stop_button { + background-position: -780px -660px; +} +.emoji-stopwatch { + background-position: -780px -680px; +} +.emoji-straight_ruler { + background-position: -780px -700px; +} +.emoji-strawberry { + background-position: -780px -720px; +} +.emoji-stuck_out_tongue { + background-position: -780px -740px; +} +.emoji-stuck_out_tongue_closed_eyes { + background-position: -780px -760px; +} +.emoji-stuck_out_tongue_winking_eye { + background-position: 0 -780px; +} +.emoji-stuffed_flatbread { + background-position: -20px -780px; +} +.emoji-sun_with_face { + background-position: -40px -780px; +} +.emoji-sunflower { + background-position: -60px -780px; +} +.emoji-sunglasses { + background-position: -80px -780px; +} +.emoji-sunny { + background-position: -100px -780px; +} +.emoji-sunrise { + background-position: -120px -780px; +} +.emoji-sunrise_over_mountains { + background-position: -140px -780px; +} +.emoji-surfer { + background-position: -160px -780px; +} +.emoji-surfer_tone1 { + background-position: -180px -780px; +} +.emoji-surfer_tone2 { + background-position: -200px -780px; +} +.emoji-surfer_tone3 { + background-position: -220px -780px; +} +.emoji-surfer_tone4 { + background-position: -240px -780px; +} +.emoji-surfer_tone5 { + background-position: -260px -780px; +} +.emoji-sushi { + background-position: -280px -780px; +} +.emoji-suspension_railway { + background-position: -300px -780px; +} +.emoji-sweat { + background-position: -320px -780px; +} +.emoji-sweat_drops { + background-position: -340px -780px; +} +.emoji-sweat_smile { + background-position: -360px -780px; +} +.emoji-sweet_potato { + background-position: -380px -780px; +} +.emoji-swimmer { + background-position: -400px -780px; +} +.emoji-swimmer_tone1 { + background-position: -420px -780px; +} +.emoji-swimmer_tone2 { + background-position: -440px -780px; +} +.emoji-swimmer_tone3 { + background-position: -460px -780px; +} +.emoji-swimmer_tone4 { + background-position: -480px -780px; +} +.emoji-swimmer_tone5 { + background-position: -500px -780px; +} +.emoji-symbols { + background-position: -520px -780px; +} +.emoji-synagogue { + background-position: -540px -780px; +} +.emoji-syringe { + background-position: -560px -780px; +} +.emoji-taco { + background-position: -580px -780px; +} +.emoji-tada { + background-position: -600px -780px; +} +.emoji-tanabata_tree { + background-position: -620px -780px; +} +.emoji-tangerine { + background-position: -640px -780px; +} +.emoji-taurus { + background-position: -660px -780px; +} +.emoji-taxi { + background-position: -680px -780px; +} +.emoji-tea { + background-position: -700px -780px; +} +.emoji-telephone { + background-position: -720px -780px; +} +.emoji-telephone_receiver { + background-position: -740px -780px; +} +.emoji-telescope { + background-position: -760px -780px; +} +.emoji-ten { + background-position: -780px -780px; +} +.emoji-tennis { + background-position: -800px 0; +} +.emoji-tent { + background-position: -800px -20px; +} +.emoji-thermometer { + background-position: -800px -40px; +} +.emoji-thermometer_face { + background-position: -800px -60px; +} +.emoji-thinking { + background-position: -800px -80px; +} +.emoji-third_place { + background-position: -800px -100px; +} +.emoji-thought_balloon { + background-position: -800px -120px; +} +.emoji-three { + background-position: -800px -140px; +} +.emoji-thumbsdown { + background-position: -800px -160px; +} +.emoji-thumbsdown_tone1 { + background-position: -800px -180px; +} +.emoji-thumbsdown_tone2 { + background-position: -800px -200px; +} +.emoji-thumbsdown_tone3 { + background-position: -800px -220px; +} +.emoji-thumbsdown_tone4 { + background-position: -800px -240px; +} +.emoji-thumbsdown_tone5 { + background-position: -800px -260px; +} +.emoji-thumbsup { + background-position: -800px -280px; +} +.emoji-thumbsup_tone1 { + background-position: -800px -300px; +} +.emoji-thumbsup_tone2 { + background-position: -800px -320px; +} +.emoji-thumbsup_tone3 { + background-position: -800px -340px; +} +.emoji-thumbsup_tone4 { + background-position: -800px -360px; +} +.emoji-thumbsup_tone5 { + background-position: -800px -380px; +} +.emoji-thunder_cloud_rain { + background-position: -800px -400px; +} +.emoji-ticket { + background-position: -800px -420px; +} +.emoji-tickets { + background-position: -800px -440px; +} +.emoji-tiger { + background-position: -800px -460px; +} +.emoji-tiger2 { + background-position: -800px -480px; +} +.emoji-timer { + background-position: -800px -500px; +} +.emoji-tired_face { + background-position: -800px -520px; +} +.emoji-tm { + background-position: -800px -540px; +} +.emoji-toilet { + background-position: -800px -560px; +} +.emoji-tokyo_tower { + background-position: -800px -580px; +} +.emoji-tomato { + background-position: -800px -600px; +} +.emoji-tone1 { + background-position: -800px -620px; +} +.emoji-tone2 { + background-position: -800px -640px; +} +.emoji-tone3 { + background-position: -800px -660px; +} +.emoji-tone4 { + background-position: -800px -680px; +} +.emoji-tone5 { + background-position: -800px -700px; +} +.emoji-tongue { + background-position: -800px -720px; +} +.emoji-tools { + background-position: -800px -740px; +} +.emoji-top { + background-position: -800px -760px; +} +.emoji-tophat { + background-position: -800px -780px; +} +.emoji-track_next { + background-position: 0 -800px; +} +.emoji-track_previous { + background-position: -20px -800px; +} +.emoji-trackball { + background-position: -40px -800px; +} +.emoji-tractor { + background-position: -60px -800px; +} +.emoji-traffic_light { + background-position: -80px -800px; +} +.emoji-train { + background-position: -100px -800px; +} +.emoji-train2 { + background-position: -120px -800px; +} +.emoji-tram { + background-position: -140px -800px; +} +.emoji-triangular_flag_on_post { + background-position: -160px -800px; +} +.emoji-triangular_ruler { + background-position: -180px -800px; +} +.emoji-trident { + background-position: -200px -800px; +} +.emoji-triumph { + background-position: -220px -800px; +} +.emoji-trolleybus { + background-position: -240px -800px; +} +.emoji-trophy { + background-position: -260px -800px; +} +.emoji-tropical_drink { + background-position: -280px -800px; +} +.emoji-tropical_fish { + background-position: -300px -800px; +} +.emoji-truck { + background-position: -320px -800px; +} +.emoji-trumpet { + background-position: -340px -800px; +} +.emoji-tulip { + background-position: -360px -800px; +} +.emoji-tumbler_glass { + background-position: -380px -800px; +} +.emoji-turkey { + background-position: -400px -800px; +} +.emoji-turtle { + background-position: -420px -800px; +} +.emoji-tv { + background-position: -440px -800px; +} +.emoji-twisted_rightwards_arrows { + background-position: -460px -800px; +} +.emoji-two { + background-position: -480px -800px; +} +.emoji-two_hearts { + background-position: -500px -800px; +} +.emoji-two_men_holding_hands { + background-position: -520px -800px; +} +.emoji-two_women_holding_hands { + background-position: -540px -800px; +} +.emoji-u5272 { + background-position: -560px -800px; +} +.emoji-u5408 { + background-position: -580px -800px; +} +.emoji-u55b6 { + background-position: -600px -800px; +} +.emoji-u6307 { + background-position: -620px -800px; +} +.emoji-u6708 { + background-position: -640px -800px; +} +.emoji-u6709 { + background-position: -660px -800px; +} +.emoji-u6e80 { + background-position: -680px -800px; +} +.emoji-u7121 { + background-position: -700px -800px; +} +.emoji-u7533 { + background-position: -720px -800px; +} +.emoji-u7981 { + background-position: -740px -800px; +} +.emoji-u7a7a { + background-position: -760px -800px; +} +.emoji-umbrella { + background-position: -780px -800px; +} +.emoji-umbrella2 { + background-position: -800px -800px; +} +.emoji-unamused { + background-position: -820px 0; +} +.emoji-underage { + background-position: -820px -20px; +} +.emoji-unicorn { + background-position: -820px -40px; +} +.emoji-unlock { + background-position: -820px -60px; +} +.emoji-up { + background-position: -820px -80px; +} +.emoji-upside_down { + background-position: -820px -100px; +} +.emoji-urn { + background-position: -820px -120px; +} +.emoji-v { + background-position: -820px -140px; +} +.emoji-v_tone1 { + background-position: -820px -160px; +} +.emoji-v_tone2 { + background-position: -820px -180px; +} +.emoji-v_tone3 { + background-position: -820px -200px; +} +.emoji-v_tone4 { + background-position: -820px -220px; +} +.emoji-v_tone5 { + background-position: -820px -240px; +} +.emoji-vertical_traffic_light { + background-position: -820px -260px; +} +.emoji-vhs { + background-position: -820px -280px; +} +.emoji-vibration_mode { + background-position: -820px -300px; +} +.emoji-video_camera { + background-position: -820px -320px; +} +.emoji-video_game { + background-position: -820px -340px; +} +.emoji-violin { + background-position: -820px -360px; +} +.emoji-virgo { + background-position: -820px -380px; +} +.emoji-volcano { + background-position: -820px -400px; +} +.emoji-volleyball { + background-position: -820px -420px; +} +.emoji-vs { + background-position: -820px -440px; +} +.emoji-vulcan { + background-position: -820px -460px; +} +.emoji-vulcan_tone1 { + background-position: -820px -480px; +} +.emoji-vulcan_tone2 { + background-position: -820px -500px; +} +.emoji-vulcan_tone3 { + background-position: -820px -520px; +} +.emoji-vulcan_tone4 { + background-position: -820px -540px; +} +.emoji-vulcan_tone5 { + background-position: -820px -560px; +} +.emoji-walking { + background-position: -820px -580px; +} +.emoji-walking_tone1 { + background-position: -820px -600px; +} +.emoji-walking_tone2 { + background-position: -820px -620px; +} +.emoji-walking_tone3 { + background-position: -820px -640px; +} +.emoji-walking_tone4 { + background-position: -820px -660px; +} +.emoji-walking_tone5 { + background-position: -820px -680px; +} +.emoji-waning_crescent_moon { + background-position: -820px -700px; +} +.emoji-waning_gibbous_moon { + background-position: -820px -720px; +} +.emoji-warning { + background-position: -820px -740px; +} +.emoji-wastebasket { + background-position: -820px -760px; +} +.emoji-watch { + background-position: -820px -780px; +} +.emoji-water_buffalo { + background-position: -820px -800px; +} +.emoji-water_polo { + background-position: 0 -820px; +} +.emoji-water_polo_tone1 { + background-position: -20px -820px; +} +.emoji-water_polo_tone2 { + background-position: -40px -820px; +} +.emoji-water_polo_tone3 { + background-position: -60px -820px; +} +.emoji-water_polo_tone4 { + background-position: -80px -820px; +} +.emoji-water_polo_tone5 { + background-position: -100px -820px; +} +.emoji-watermelon { + background-position: -120px -820px; +} +.emoji-wave { + background-position: -140px -820px; +} +.emoji-wave_tone1 { + background-position: -160px -820px; +} +.emoji-wave_tone2 { + background-position: -180px -820px; +} +.emoji-wave_tone3 { + background-position: -200px -820px; +} +.emoji-wave_tone4 { + background-position: -220px -820px; +} +.emoji-wave_tone5 { + background-position: -240px -820px; +} +.emoji-wavy_dash { + background-position: -260px -820px; +} +.emoji-waxing_crescent_moon { + background-position: -280px -820px; +} +.emoji-waxing_gibbous_moon { + background-position: -300px -820px; +} +.emoji-wc { + background-position: -320px -820px; +} +.emoji-weary { + background-position: -340px -820px; +} +.emoji-wedding { + background-position: -360px -820px; +} +.emoji-whale { + background-position: -380px -820px; +} +.emoji-whale2 { + background-position: -400px -820px; +} +.emoji-wheel_of_dharma { + background-position: -420px -820px; +} +.emoji-wheelchair { + background-position: -440px -820px; +} +.emoji-white_check_mark { + background-position: -460px -820px; +} +.emoji-white_circle { + background-position: -480px -820px; +} +.emoji-white_flower { + background-position: -500px -820px; +} +.emoji-white_large_square { + background-position: -520px -820px; +} +.emoji-white_medium_small_square { + background-position: -540px -820px; +} +.emoji-white_medium_square { + background-position: -560px -820px; +} +.emoji-white_small_square { + background-position: -580px -820px; +} +.emoji-white_square_button { + background-position: -600px -820px; +} +.emoji-white_sun_cloud { + background-position: -620px -820px; +} +.emoji-white_sun_rain_cloud { + background-position: -640px -820px; +} +.emoji-white_sun_small_cloud { + background-position: -660px -820px; +} +.emoji-wilted_rose { + background-position: -680px -820px; +} +.emoji-wind_blowing_face { + background-position: -700px -820px; +} +.emoji-wind_chime { + background-position: -720px -820px; +} +.emoji-wine_glass { + background-position: -740px -820px; +} +.emoji-wink { + background-position: -760px -820px; +} +.emoji-wolf { + background-position: -780px -820px; +} +.emoji-woman { + background-position: -800px -820px; +} +.emoji-woman_tone1 { + background-position: -820px -820px; +} +.emoji-woman_tone2 { + background-position: -840px 0; +} +.emoji-woman_tone3 { + background-position: -840px -20px; +} +.emoji-woman_tone4 { + background-position: -840px -40px; +} +.emoji-woman_tone5 { + background-position: -840px -60px; +} +.emoji-womans_clothes { + background-position: -840px -80px; +} +.emoji-womans_hat { + background-position: -840px -100px; +} +.emoji-womens { + background-position: -840px -120px; +} +.emoji-worried { + background-position: -840px -140px; +} +.emoji-wrench { + background-position: -840px -160px; +} +.emoji-wrestlers { + background-position: -840px -180px; +} +.emoji-wrestlers_tone1 { + background-position: -840px -200px; +} +.emoji-wrestlers_tone2 { + background-position: -840px -220px; +} +.emoji-wrestlers_tone3 { + background-position: -840px -240px; +} +.emoji-wrestlers_tone4 { + background-position: -840px -260px; +} +.emoji-wrestlers_tone5 { + background-position: -840px -280px; +} +.emoji-writing_hand { + background-position: -840px -300px; +} +.emoji-writing_hand_tone1 { + background-position: -840px -320px; +} +.emoji-writing_hand_tone2 { + background-position: -840px -340px; +} +.emoji-writing_hand_tone3 { + background-position: -840px -360px; +} +.emoji-writing_hand_tone4 { + background-position: -840px -380px; +} +.emoji-writing_hand_tone5 { + background-position: -840px -400px; +} +.emoji-x { + background-position: -840px -420px; +} +.emoji-yellow_heart { + background-position: -840px -440px; +} +.emoji-yen { + background-position: -840px -460px; +} +.emoji-yin_yang { + background-position: -840px -480px; +} +.emoji-yum { + background-position: -840px -500px; +} +.emoji-zap { + background-position: -840px -520px; +} +.emoji-zero { + background-position: -840px -540px; +} +.emoji-zipper_mouth { + background-position: -840px -560px; +} +.emoji-100 { + background-position: -840px -580px; +} + +.emoji-icon { + background-image: image-url('emoji.png'); + background-repeat: no-repeat; + color: transparent; + text-indent: -99em; + height: 20px; + width: 20px; + + @media only screen and (-webkit-min-device-pixel-ratio: 2), + only screen and (min--moz-device-pixel-ratio: 2), + only screen and (-o-min-device-pixel-ratio: 2/1), + only screen and (min-device-pixel-ratio: 2), + only screen and (min-resolution: 192dpi), + only screen and (min-resolution: 2dppx) { + background-image: image-url('emoji@2x.png'); + background-size: 860px 840px; + } +} diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 2fccfa4011c..360dcb6afef 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -1,64 +1,63 @@ -@import "framework/variables"; -@import "framework/mixins"; +@import 'framework/variables'; +@import 'framework/mixins'; @import 'framework/tw_bootstrap_variables'; @import 'framework/tw_bootstrap'; -@import "framework/layout"; +@import 'framework/layout'; -@import "framework/animations"; -@import "framework/vue_transitions"; -@import "framework/avatar"; -@import "framework/asciidoctor"; -@import "framework/banner"; -@import "framework/blocks"; -@import "framework/buttons"; -@import "framework/badges"; -@import "framework/calendar"; -@import "framework/callout"; -@import "framework/common"; -@import "framework/dropdowns"; -@import "framework/files"; -@import "framework/filters"; -@import "framework/flash"; -@import "framework/forms"; -@import "framework/gfm"; -@import "framework/gitlab_theme"; -@import "framework/header"; -@import "framework/highlight"; -@import "framework/issue_box"; -@import "framework/jquery"; -@import "framework/lists"; -@import "framework/logo"; -@import "framework/markdown_area"; -@import "framework/media_object"; -@import "framework/mobile"; -@import "framework/modal"; -@import "framework/pagination"; -@import "framework/panels"; -@import "framework/popup"; -@import "framework/secondary_navigation_elements"; -@import "framework/selects"; -@import "framework/sidebar"; -@import "framework/contextual_sidebar"; -@import "framework/tables"; -@import "framework/notes"; -@import "framework/tabs"; -@import "framework/timeline"; -@import "framework/tooltips"; -@import "framework/toggle"; -@import "framework/typography"; -@import "framework/zen"; -@import "framework/blank"; -@import "framework/wells"; -@import "framework/page_header"; -@import "framework/awards"; -@import "framework/images"; -@import "framework/broadcast_messages"; -@import "framework/emojis"; -@import "framework/emoji_sprites"; -@import "framework/icons"; -@import "framework/snippets"; -@import "framework/memory_graph"; -@import "framework/responsive_tables"; -@import "framework/stacked_progress_bar"; -@import "framework/ci_variable_list"; -@import "framework/feature_highlight"; +@import 'framework/animations'; +@import 'framework/vue_transitions'; +@import 'framework/avatar'; +@import 'framework/asciidoctor'; +@import 'framework/banner'; +@import 'framework/blocks'; +@import 'framework/buttons'; +@import 'framework/badges'; +@import 'framework/calendar'; +@import 'framework/callout'; +@import 'framework/common'; +@import 'framework/dropdowns'; +@import 'framework/files'; +@import 'framework/filters'; +@import 'framework/flash'; +@import 'framework/forms'; +@import 'framework/gfm'; +@import 'framework/gitlab_theme'; +@import 'framework/header'; +@import 'framework/highlight'; +@import 'framework/issue_box'; +@import 'framework/jquery'; +@import 'framework/lists'; +@import 'framework/logo'; +@import 'framework/markdown_area'; +@import 'framework/media_object'; +@import 'framework/mobile'; +@import 'framework/modal'; +@import 'framework/pagination'; +@import 'framework/panels'; +@import 'framework/popup'; +@import 'framework/secondary_navigation_elements'; +@import 'framework/selects'; +@import 'framework/sidebar'; +@import 'framework/contextual_sidebar'; +@import 'framework/tables'; +@import 'framework/notes'; +@import 'framework/tabs'; +@import 'framework/timeline'; +@import 'framework/tooltips'; +@import 'framework/toggle'; +@import 'framework/typography'; +@import 'framework/zen'; +@import 'framework/blank'; +@import 'framework/wells'; +@import 'framework/page_header'; +@import 'framework/awards'; +@import 'framework/images'; +@import 'framework/broadcast_messages'; +@import 'framework/emojis'; +@import 'framework/icons'; +@import 'framework/snippets'; +@import 'framework/memory_graph'; +@import 'framework/responsive_tables'; +@import 'framework/stacked_progress_bar'; +@import 'framework/ci_variable_list'; +@import 'framework/feature_highlight'; diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index c5c7afe25be..c60f65e7a85 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -46,7 +46,7 @@ } &.middle-block { - margin-top: 0; + margin-top: $gl-padding-24; margin-bottom: 0; } @@ -61,7 +61,7 @@ } &.footer-block { - margin-top: 0; + margin-top: $gl-padding-24; border-bottom: 0; margin-bottom: -$gl-padding; } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index e058a0b35b7..2faea55a5f5 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -452,6 +452,7 @@ img.emoji { /** COMMON CLASSES **/ .prepend-top-0 { margin-top: 0; } +.prepend-top-2 { margin-top: 2px; } .prepend-top-5 { margin-top: 5px; } .prepend-top-8 { margin-top: $grid-size; } .prepend-top-10 { margin-top: 10px; } diff --git a/app/assets/stylesheets/framework/emoji_sprites.scss b/app/assets/stylesheets/framework/emoji_sprites.scss deleted file mode 100644 index 0174e17b660..00000000000 --- a/app/assets/stylesheets/framework/emoji_sprites.scss +++ /dev/null @@ -1,1813 +0,0 @@ -.emoji-zzz { background-position: 0 0; } -.emoji-1234 { background-position: -20px 0; } -.emoji-1F627 { background-position: 0 -20px; } -.emoji-8ball { background-position: -20px -20px; } -.emoji-a { background-position: -40px 0; } -.emoji-ab { background-position: -40px -20px; } -.emoji-abc { background-position: 0 -40px; } -.emoji-abcd { background-position: -20px -40px; } -.emoji-accept { background-position: -40px -40px; } -.emoji-aerial_tramway { background-position: -60px 0; } -.emoji-airplane { background-position: -60px -20px; } -.emoji-airplane_arriving { background-position: -60px -40px; } -.emoji-airplane_departure { background-position: 0 -60px; } -.emoji-airplane_small { background-position: -20px -60px; } -.emoji-alarm_clock { background-position: -40px -60px; } -.emoji-alembic { background-position: -60px -60px; } -.emoji-alien { background-position: -80px 0; } -.emoji-ambulance { background-position: -80px -20px; } -.emoji-amphora { background-position: -80px -40px; } -.emoji-anchor { background-position: -80px -60px; } -.emoji-angel { background-position: 0 -80px; } -.emoji-angel_tone1 { background-position: -20px -80px; } -.emoji-angel_tone2 { background-position: -40px -80px; } -.emoji-angel_tone3 { background-position: -60px -80px; } -.emoji-angel_tone4 { background-position: -80px -80px; } -.emoji-angel_tone5 { background-position: -100px 0; } -.emoji-anger { background-position: -100px -20px; } -.emoji-anger_right { background-position: -100px -40px; } -.emoji-angry { background-position: -100px -60px; } -.emoji-ant { background-position: -100px -80px; } -.emoji-apple { background-position: 0 -100px; } -.emoji-aquarius { background-position: -20px -100px; } -.emoji-aries { background-position: -40px -100px; } -.emoji-arrow_backward { background-position: -60px -100px; } -.emoji-arrow_double_down { background-position: -80px -100px; } -.emoji-arrow_double_up { background-position: -100px -100px; } -.emoji-arrow_down { background-position: -120px 0; } -.emoji-arrow_down_small { background-position: -120px -20px; } -.emoji-arrow_forward { background-position: -120px -40px; } -.emoji-arrow_heading_down { background-position: -120px -60px; } -.emoji-arrow_heading_up { background-position: -120px -80px; } -.emoji-arrow_left { background-position: -120px -100px; } -.emoji-arrow_lower_left { background-position: 0 -120px; } -.emoji-arrow_lower_right { background-position: -20px -120px; } -.emoji-arrow_right { background-position: -40px -120px; } -.emoji-arrow_right_hook { background-position: -60px -120px; } -.emoji-arrow_up { background-position: -80px -120px; } -.emoji-arrow_up_down { background-position: -100px -120px; } -.emoji-arrow_up_small { background-position: -120px -120px; } -.emoji-arrow_upper_left { background-position: -140px 0; } -.emoji-arrow_upper_right { background-position: -140px -20px; } -.emoji-arrows_clockwise { background-position: -140px -40px; } -.emoji-arrows_counterclockwise { background-position: -140px -60px; } -.emoji-art { background-position: -140px -80px; } -.emoji-articulated_lorry { background-position: -140px -100px; } -.emoji-asterisk { background-position: -140px -120px; } -.emoji-astonished { background-position: 0 -140px; } -.emoji-athletic_shoe { background-position: -20px -140px; } -.emoji-atm { background-position: -40px -140px; } -.emoji-atom { background-position: -60px -140px; } -.emoji-avocado { background-position: -80px -140px; } -.emoji-b { background-position: -100px -140px; } -.emoji-baby { background-position: -120px -140px; } -.emoji-baby_bottle { background-position: -140px -140px; } -.emoji-baby_chick { background-position: -160px 0; } -.emoji-baby_symbol { background-position: -160px -20px; } -.emoji-baby_tone1 { background-position: -160px -40px; } -.emoji-baby_tone2 { background-position: -160px -60px; } -.emoji-baby_tone3 { background-position: -160px -80px; } -.emoji-baby_tone4 { background-position: -160px -100px; } -.emoji-baby_tone5 { background-position: -160px -120px; } -.emoji-back { background-position: -160px -140px; } -.emoji-bacon { background-position: 0 -160px; } -.emoji-badminton { background-position: -20px -160px; } -.emoji-baggage_claim { background-position: -40px -160px; } -.emoji-balloon { background-position: -60px -160px; } -.emoji-ballot_box { background-position: -80px -160px; } -.emoji-ballot_box_with_check { background-position: -100px -160px; } -.emoji-bamboo { background-position: -120px -160px; } -.emoji-banana { background-position: -140px -160px; } -.emoji-bangbang { background-position: -160px -160px; } -.emoji-bank { background-position: -180px 0; } -.emoji-bar_chart { background-position: -180px -20px; } -.emoji-barber { background-position: -180px -40px; } -.emoji-baseball { background-position: -180px -60px; } -.emoji-basketball { background-position: -180px -80px; } -.emoji-basketball_player { background-position: -180px -100px; } -.emoji-basketball_player_tone1 { background-position: -180px -120px; } -.emoji-basketball_player_tone2 { background-position: -180px -140px; } -.emoji-basketball_player_tone3 { background-position: -180px -160px; } -.emoji-basketball_player_tone4 { background-position: 0 -180px; } -.emoji-basketball_player_tone5 { background-position: -20px -180px; } -.emoji-bat { background-position: -40px -180px; } -.emoji-bath { background-position: -60px -180px; } -.emoji-bath_tone1 { background-position: -80px -180px; } -.emoji-bath_tone2 { background-position: -100px -180px; } -.emoji-bath_tone3 { background-position: -120px -180px; } -.emoji-bath_tone4 { background-position: -140px -180px; } -.emoji-bath_tone5 { background-position: -160px -180px; } -.emoji-bathtub { background-position: -180px -180px; } -.emoji-battery { background-position: -200px 0; } -.emoji-beach { background-position: -200px -20px; } -.emoji-beach_umbrella { background-position: -200px -40px; } -.emoji-bear { background-position: -200px -60px; } -.emoji-bed { background-position: -200px -80px; } -.emoji-bee { background-position: -200px -100px; } -.emoji-beer { background-position: -200px -120px; } -.emoji-beers { background-position: -200px -140px; } -.emoji-beetle { background-position: -200px -160px; } -.emoji-beginner { background-position: -200px -180px; } -.emoji-bell { background-position: 0 -200px; } -.emoji-bellhop { background-position: -20px -200px; } -.emoji-bento { background-position: -40px -200px; } -.emoji-bicyclist { background-position: -60px -200px; } -.emoji-bicyclist_tone1 { background-position: -80px -200px; } -.emoji-bicyclist_tone2 { background-position: -100px -200px; } -.emoji-bicyclist_tone3 { background-position: -120px -200px; } -.emoji-bicyclist_tone4 { background-position: -140px -200px; } -.emoji-bicyclist_tone5 { background-position: -160px -200px; } -.emoji-bike { background-position: -180px -200px; } -.emoji-bikini { background-position: -200px -200px; } -.emoji-biohazard { background-position: -220px 0; } -.emoji-bird { background-position: -220px -20px; } -.emoji-birthday { background-position: -220px -40px; } -.emoji-black_circle { background-position: -220px -60px; } -.emoji-black_heart { background-position: -220px -80px; } -.emoji-black_joker { background-position: -220px -100px; } -.emoji-black_large_square { background-position: -220px -120px; } -.emoji-black_medium_small_square { background-position: -220px -140px; } -.emoji-black_medium_square { background-position: -220px -160px; } -.emoji-black_nib { background-position: -220px -180px; } -.emoji-black_small_square { background-position: -220px -200px; } -.emoji-black_square_button { background-position: 0 -220px; } -.emoji-blossom { background-position: -20px -220px; } -.emoji-blowfish { background-position: -40px -220px; } -.emoji-blue_book { background-position: -60px -220px; } -.emoji-blue_car { background-position: -80px -220px; } -.emoji-blue_heart { background-position: -100px -220px; } -.emoji-blush { background-position: -120px -220px; } -.emoji-boar { background-position: -140px -220px; } -.emoji-bomb { background-position: -160px -220px; } -.emoji-book { background-position: -180px -220px; } -.emoji-bookmark { background-position: -200px -220px; } -.emoji-bookmark_tabs { background-position: -220px -220px; } -.emoji-books { background-position: -240px 0; } -.emoji-boom { background-position: -240px -20px; } -.emoji-boot { background-position: -240px -40px; } -.emoji-bouquet { background-position: -240px -60px; } -.emoji-bow { background-position: -240px -80px; } -.emoji-bow_and_arrow { background-position: -240px -100px; } -.emoji-bow_tone1 { background-position: -240px -120px; } -.emoji-bow_tone2 { background-position: -240px -140px; } -.emoji-bow_tone3 { background-position: -240px -160px; } -.emoji-bow_tone4 { background-position: -240px -180px; } -.emoji-bow_tone5 { background-position: -240px -200px; } -.emoji-bowling { background-position: -240px -220px; } -.emoji-boxing_glove { background-position: 0 -240px; } -.emoji-boy { background-position: -20px -240px; } -.emoji-boy_tone1 { background-position: -40px -240px; } -.emoji-boy_tone2 { background-position: -60px -240px; } -.emoji-boy_tone3 { background-position: -80px -240px; } -.emoji-boy_tone4 { background-position: -100px -240px; } -.emoji-boy_tone5 { background-position: -120px -240px; } -.emoji-bread { background-position: -140px -240px; } -.emoji-bride_with_veil { background-position: -160px -240px; } -.emoji-bride_with_veil_tone1 { background-position: -180px -240px; } -.emoji-bride_with_veil_tone2 { background-position: -200px -240px; } -.emoji-bride_with_veil_tone3 { background-position: -220px -240px; } -.emoji-bride_with_veil_tone4 { background-position: -240px -240px; } -.emoji-bride_with_veil_tone5 { background-position: -260px 0; } -.emoji-bridge_at_night { background-position: -260px -20px; } -.emoji-briefcase { background-position: -260px -40px; } -.emoji-broken_heart { background-position: -260px -60px; } -.emoji-bug { background-position: -260px -80px; } -.emoji-bulb { background-position: -260px -100px; } -.emoji-bullettrain_front { background-position: -260px -120px; } -.emoji-bullettrain_side { background-position: -260px -140px; } -.emoji-burrito { background-position: -260px -160px; } -.emoji-bus { background-position: -260px -180px; } -.emoji-busstop { background-position: -260px -200px; } -.emoji-bust_in_silhouette { background-position: -260px -220px; } -.emoji-busts_in_silhouette { background-position: -260px -240px; } -.emoji-butterfly { background-position: 0 -260px; } -.emoji-cactus { background-position: -20px -260px; } -.emoji-cake { background-position: -40px -260px; } -.emoji-calendar { background-position: -60px -260px; } -.emoji-calendar_spiral { background-position: -80px -260px; } -.emoji-call_me { background-position: -100px -260px; } -.emoji-call_me_tone1 { background-position: -120px -260px; } -.emoji-call_me_tone2 { background-position: -140px -260px; } -.emoji-call_me_tone3 { background-position: -160px -260px; } -.emoji-call_me_tone4 { background-position: -180px -260px; } -.emoji-call_me_tone5 { background-position: -200px -260px; } -.emoji-calling { background-position: -220px -260px; } -.emoji-camel { background-position: -240px -260px; } -.emoji-camera { background-position: -260px -260px; } -.emoji-camera_with_flash { background-position: -280px 0; } -.emoji-camping { background-position: -280px -20px; } -.emoji-cancer { background-position: -280px -40px; } -.emoji-candle { background-position: -280px -60px; } -.emoji-candy { background-position: -280px -80px; } -.emoji-canoe { background-position: -280px -100px; } -.emoji-capital_abcd { background-position: -280px -120px; } -.emoji-capricorn { background-position: -280px -140px; } -.emoji-card_box { background-position: -280px -160px; } -.emoji-card_index { background-position: -280px -180px; } -.emoji-carousel_horse { background-position: -280px -200px; } -.emoji-carrot { background-position: -280px -220px; } -.emoji-cartwheel { background-position: -280px -240px; } -.emoji-cartwheel_tone1 { background-position: -280px -260px; } -.emoji-cartwheel_tone2 { background-position: 0 -280px; } -.emoji-cartwheel_tone3 { background-position: -20px -280px; } -.emoji-cartwheel_tone4 { background-position: -40px -280px; } -.emoji-cartwheel_tone5 { background-position: -60px -280px; } -.emoji-cat { background-position: -80px -280px; } -.emoji-cat2 { background-position: -100px -280px; } -.emoji-cd { background-position: -120px -280px; } -.emoji-chains { background-position: -140px -280px; } -.emoji-champagne { background-position: -160px -280px; } -.emoji-champagne_glass { background-position: -180px -280px; } -.emoji-chart { background-position: -200px -280px; } -.emoji-chart_with_downwards_trend { background-position: -220px -280px; } -.emoji-chart_with_upwards_trend { background-position: -240px -280px; } -.emoji-checkered_flag { background-position: -260px -280px; } -.emoji-cheese { background-position: -280px -280px; } -.emoji-cherries { background-position: -300px 0; } -.emoji-cherry_blossom { background-position: -300px -20px; } -.emoji-chestnut { background-position: -300px -40px; } -.emoji-chicken { background-position: -300px -60px; } -.emoji-children_crossing { background-position: -300px -80px; } -.emoji-chipmunk { background-position: -300px -100px; } -.emoji-chocolate_bar { background-position: -300px -120px; } -.emoji-christmas_tree { background-position: -300px -140px; } -.emoji-church { background-position: -300px -160px; } -.emoji-cinema { background-position: -300px -180px; } -.emoji-circus_tent { background-position: -300px -200px; } -.emoji-city_dusk { background-position: -300px -220px; } -.emoji-city_sunset { background-position: -300px -240px; } -.emoji-cityscape { background-position: -300px -260px; } -.emoji-cl { background-position: -300px -280px; } -.emoji-clap { background-position: 0 -300px; } -.emoji-clap_tone1 { background-position: -20px -300px; } -.emoji-clap_tone2 { background-position: -40px -300px; } -.emoji-clap_tone3 { background-position: -60px -300px; } -.emoji-clap_tone4 { background-position: -80px -300px; } -.emoji-clap_tone5 { background-position: -100px -300px; } -.emoji-clapper { background-position: -120px -300px; } -.emoji-classical_building { background-position: -140px -300px; } -.emoji-clipboard { background-position: -160px -300px; } -.emoji-clock { background-position: -180px -300px; } -.emoji-clock1 { background-position: -200px -300px; } -.emoji-clock10 { background-position: -220px -300px; } -.emoji-clock1030 { background-position: -240px -300px; } -.emoji-clock11 { background-position: -260px -300px; } -.emoji-clock1130 { background-position: -280px -300px; } -.emoji-clock12 { background-position: -300px -300px; } -.emoji-clock1230 { background-position: -320px 0; } -.emoji-clock130 { background-position: -320px -20px; } -.emoji-clock2 { background-position: -320px -40px; } -.emoji-clock230 { background-position: -320px -60px; } -.emoji-clock3 { background-position: -320px -80px; } -.emoji-clock330 { background-position: -320px -100px; } -.emoji-clock4 { background-position: -320px -120px; } -.emoji-clock430 { background-position: -320px -140px; } -.emoji-clock5 { background-position: -320px -160px; } -.emoji-clock530 { background-position: -320px -180px; } -.emoji-clock6 { background-position: -320px -200px; } -.emoji-clock630 { background-position: -320px -220px; } -.emoji-clock7 { background-position: -320px -240px; } -.emoji-clock730 { background-position: -320px -260px; } -.emoji-clock8 { background-position: -320px -280px; } -.emoji-clock830 { background-position: -320px -300px; } -.emoji-clock9 { background-position: 0 -320px; } -.emoji-clock930 { background-position: -20px -320px; } -.emoji-closed_book { background-position: -40px -320px; } -.emoji-closed_lock_with_key { background-position: -60px -320px; } -.emoji-closed_umbrella { background-position: -80px -320px; } -.emoji-cloud { background-position: -100px -320px; } -.emoji-cloud_lightning { background-position: -120px -320px; } -.emoji-cloud_rain { background-position: -140px -320px; } -.emoji-cloud_snow { background-position: -160px -320px; } -.emoji-cloud_tornado { background-position: -180px -320px; } -.emoji-clown { background-position: -200px -320px; } -.emoji-clubs { background-position: -220px -320px; } -.emoji-cocktail { background-position: -240px -320px; } -.emoji-coffee { background-position: -260px -320px; } -.emoji-coffin { background-position: -280px -320px; } -.emoji-cold_sweat { background-position: -300px -320px; } -.emoji-comet { background-position: -320px -320px; } -.emoji-compression { background-position: -340px 0; } -.emoji-computer { background-position: -340px -20px; } -.emoji-confetti_ball { background-position: -340px -40px; } -.emoji-confounded { background-position: -340px -60px; } -.emoji-confused { background-position: -340px -80px; } -.emoji-congratulations { background-position: -340px -100px; } -.emoji-construction { background-position: -340px -120px; } -.emoji-construction_site { background-position: -340px -140px; } -.emoji-construction_worker { background-position: -340px -160px; } -.emoji-construction_worker_tone1 { background-position: -340px -180px; } -.emoji-construction_worker_tone2 { background-position: -340px -200px; } -.emoji-construction_worker_tone3 { background-position: -340px -220px; } -.emoji-construction_worker_tone4 { background-position: -340px -240px; } -.emoji-construction_worker_tone5 { background-position: -340px -260px; } -.emoji-control_knobs { background-position: -340px -280px; } -.emoji-convenience_store { background-position: -340px -300px; } -.emoji-cookie { background-position: -340px -320px; } -.emoji-cooking { background-position: 0 -340px; } -.emoji-cool { background-position: -20px -340px; } -.emoji-cop { background-position: -40px -340px; } -.emoji-cop_tone1 { background-position: -60px -340px; } -.emoji-cop_tone2 { background-position: -80px -340px; } -.emoji-cop_tone3 { background-position: -100px -340px; } -.emoji-cop_tone4 { background-position: -120px -340px; } -.emoji-cop_tone5 { background-position: -140px -340px; } -.emoji-copyright { background-position: -160px -340px; } -.emoji-corn { background-position: -180px -340px; } -.emoji-couch { background-position: -200px -340px; } -.emoji-couple { background-position: -220px -340px; } -.emoji-couple_mm { background-position: -240px -340px; } -.emoji-couple_with_heart { background-position: -260px -340px; } -.emoji-couple_ww { background-position: -280px -340px; } -.emoji-couplekiss { background-position: -300px -340px; } -.emoji-cow { background-position: -320px -340px; } -.emoji-cow2 { background-position: -340px -340px; } -.emoji-cowboy { background-position: -360px 0; } -.emoji-crab { background-position: -360px -20px; } -.emoji-crayon { background-position: -360px -40px; } -.emoji-credit_card { background-position: -360px -60px; } -.emoji-crescent_moon { background-position: -360px -80px; } -.emoji-cricket { background-position: -360px -100px; } -.emoji-crocodile { background-position: -360px -120px; } -.emoji-croissant { background-position: -360px -140px; } -.emoji-cross { background-position: -360px -160px; } -.emoji-crossed_flags { background-position: -360px -180px; } -.emoji-crossed_swords { background-position: -360px -200px; } -.emoji-crown { background-position: -360px -220px; } -.emoji-cruise_ship { background-position: -360px -240px; } -.emoji-cry { background-position: -360px -260px; } -.emoji-crying_cat_face { background-position: -360px -280px; } -.emoji-crystal_ball { background-position: -360px -300px; } -.emoji-cucumber { background-position: -360px -320px; } -.emoji-cupid { background-position: -360px -340px; } -.emoji-curly_loop { background-position: 0 -360px; } -.emoji-currency_exchange { background-position: -20px -360px; } -.emoji-curry { background-position: -40px -360px; } -.emoji-custard { background-position: -60px -360px; } -.emoji-customs { background-position: -80px -360px; } -.emoji-cyclone { background-position: -100px -360px; } -.emoji-dagger { background-position: -120px -360px; } -.emoji-dancer { background-position: -140px -360px; } -.emoji-dancer_tone1 { background-position: -160px -360px; } -.emoji-dancer_tone2 { background-position: -180px -360px; } -.emoji-dancer_tone3 { background-position: -200px -360px; } -.emoji-dancer_tone4 { background-position: -220px -360px; } -.emoji-dancer_tone5 { background-position: -240px -360px; } -.emoji-dancers { background-position: -260px -360px; } -.emoji-dango { background-position: -280px -360px; } -.emoji-dark_sunglasses { background-position: -300px -360px; } -.emoji-dart { background-position: -320px -360px; } -.emoji-dash { background-position: -340px -360px; } -.emoji-date { background-position: -360px -360px; } -.emoji-deciduous_tree { background-position: -380px 0; } -.emoji-deer { background-position: -380px -20px; } -.emoji-department_store { background-position: -380px -40px; } -.emoji-desert { background-position: -380px -60px; } -.emoji-desktop { background-position: -380px -80px; } -.emoji-diamond_shape_with_a_dot_inside { background-position: -380px -100px; } -.emoji-diamonds { background-position: -380px -120px; } -.emoji-disappointed { background-position: -380px -140px; } -.emoji-disappointed_relieved { background-position: -380px -160px; } -.emoji-dividers { background-position: -380px -180px; } -.emoji-dizzy { background-position: -380px -200px; } -.emoji-dizzy_face { background-position: -380px -220px; } -.emoji-do_not_litter { background-position: -380px -240px; } -.emoji-dog { background-position: -380px -260px; } -.emoji-dog2 { background-position: -380px -280px; } -.emoji-dollar { background-position: -380px -300px; } -.emoji-dolls { background-position: -380px -320px; } -.emoji-dolphin { background-position: -380px -340px; } -.emoji-door { background-position: -380px -360px; } -.emoji-doughnut { background-position: 0 -380px; } -.emoji-dove { background-position: -20px -380px; } -.emoji-dragon { background-position: -40px -380px; } -.emoji-dragon_face { background-position: -60px -380px; } -.emoji-dress { background-position: -80px -380px; } -.emoji-dromedary_camel { background-position: -100px -380px; } -.emoji-drooling_face { background-position: -120px -380px; } -.emoji-droplet { background-position: -140px -380px; } -.emoji-drum { background-position: -160px -380px; } -.emoji-duck { background-position: -180px -380px; } -.emoji-dvd { background-position: -200px -380px; } -.emoji-e-mail { background-position: -220px -380px; } -.emoji-eagle { background-position: -240px -380px; } -.emoji-ear { background-position: -260px -380px; } -.emoji-ear_of_rice { background-position: -280px -380px; } -.emoji-ear_tone1 { background-position: -300px -380px; } -.emoji-ear_tone2 { background-position: -320px -380px; } -.emoji-ear_tone3 { background-position: -340px -380px; } -.emoji-ear_tone4 { background-position: -360px -380px; } -.emoji-ear_tone5 { background-position: -380px -380px; } -.emoji-earth_africa { background-position: -400px 0; } -.emoji-earth_americas { background-position: -400px -20px; } -.emoji-earth_asia { background-position: -400px -40px; } -.emoji-egg { background-position: -400px -60px; } -.emoji-eggplant { background-position: -400px -80px; } -.emoji-eight { background-position: -400px -100px; } -.emoji-eight_pointed_black_star { background-position: -400px -120px; } -.emoji-eight_spoked_asterisk { background-position: -400px -140px; } -.emoji-eject { background-position: -400px -160px; } -.emoji-electric_plug { background-position: -400px -180px; } -.emoji-elephant { background-position: -400px -200px; } -.emoji-end { background-position: -400px -220px; } -.emoji-envelope { background-position: -400px -240px; } -.emoji-envelope_with_arrow { background-position: -400px -260px; } -.emoji-euro { background-position: -400px -280px; } -.emoji-european_castle { background-position: -400px -300px; } -.emoji-european_post_office { background-position: -400px -320px; } -.emoji-evergreen_tree { background-position: -400px -340px; } -.emoji-exclamation { background-position: -400px -360px; } -.emoji-expressionless { background-position: -400px -380px; } -.emoji-eye { background-position: 0 -400px; } -.emoji-eye_in_speech_bubble { background-position: -20px -400px; } -.emoji-eyeglasses { background-position: -40px -400px; } -.emoji-eyes { background-position: -60px -400px; } -.emoji-face_palm { background-position: -80px -400px; } -.emoji-face_palm_tone1 { background-position: -100px -400px; } -.emoji-face_palm_tone2 { background-position: -120px -400px; } -.emoji-face_palm_tone3 { background-position: -140px -400px; } -.emoji-face_palm_tone4 { background-position: -160px -400px; } -.emoji-face_palm_tone5 { background-position: -180px -400px; } -.emoji-factory { background-position: -200px -400px; } -.emoji-fallen_leaf { background-position: -220px -400px; } -.emoji-family { background-position: -240px -400px; } -.emoji-family_mmb { background-position: -260px -400px; } -.emoji-family_mmbb { background-position: -280px -400px; } -.emoji-family_mmg { background-position: -300px -400px; } -.emoji-family_mmgb { background-position: -320px -400px; } -.emoji-family_mmgg { background-position: -340px -400px; } -.emoji-family_mwbb { background-position: -360px -400px; } -.emoji-family_mwg { background-position: -380px -400px; } -.emoji-family_mwgb { background-position: -400px -400px; } -.emoji-family_mwgg { background-position: -420px 0; } -.emoji-family_wwb { background-position: -420px -20px; } -.emoji-family_wwbb { background-position: -420px -40px; } -.emoji-family_wwg { background-position: -420px -60px; } -.emoji-family_wwgb { background-position: -420px -80px; } -.emoji-family_wwgg { background-position: -420px -100px; } -.emoji-fast_forward { background-position: -420px -120px; } -.emoji-fax { background-position: -420px -140px; } -.emoji-fearful { background-position: -420px -160px; } -.emoji-feet { background-position: -420px -180px; } -.emoji-fencer { background-position: -420px -200px; } -.emoji-ferris_wheel { background-position: -420px -220px; } -.emoji-ferry { background-position: -420px -240px; } -.emoji-field_hockey { background-position: -420px -260px; } -.emoji-file_cabinet { background-position: -420px -280px; } -.emoji-file_folder { background-position: -420px -300px; } -.emoji-film_frames { background-position: -420px -320px; } -.emoji-fingers_crossed { background-position: -420px -340px; } -.emoji-fingers_crossed_tone1 { background-position: -420px -360px; } -.emoji-fingers_crossed_tone2 { background-position: -420px -380px; } -.emoji-fingers_crossed_tone3 { background-position: -420px -400px; } -.emoji-fingers_crossed_tone4 { background-position: 0 -420px; } -.emoji-fingers_crossed_tone5 { background-position: -20px -420px; } -.emoji-fire { background-position: -40px -420px; } -.emoji-fire_engine { background-position: -60px -420px; } -.emoji-fireworks { background-position: -80px -420px; } -.emoji-first_place { background-position: -100px -420px; } -.emoji-first_quarter_moon { background-position: -120px -420px; } -.emoji-first_quarter_moon_with_face { background-position: -140px -420px; } -.emoji-fish { background-position: -160px -420px; } -.emoji-fish_cake { background-position: -180px -420px; } -.emoji-fishing_pole_and_fish { background-position: -200px -420px; } -.emoji-fist { background-position: -220px -420px; } -.emoji-fist_tone1 { background-position: -240px -420px; } -.emoji-fist_tone2 { background-position: -260px -420px; } -.emoji-fist_tone3 { background-position: -280px -420px; } -.emoji-fist_tone4 { background-position: -300px -420px; } -.emoji-fist_tone5 { background-position: -320px -420px; } -.emoji-five { background-position: -340px -420px; } -.emoji-flag_ac { background-position: -360px -420px; } -.emoji-flag_ad { background-position: -380px -420px; } -.emoji-flag_ae { background-position: -400px -420px; } -.emoji-flag_af { background-position: -420px -420px; } -.emoji-flag_ag { background-position: -440px 0; } -.emoji-flag_ai { background-position: -440px -20px; } -.emoji-flag_al { background-position: -440px -40px; } -.emoji-flag_am { background-position: -440px -60px; } -.emoji-flag_ao { background-position: -440px -80px; } -.emoji-flag_aq { background-position: -440px -100px; } -.emoji-flag_ar { background-position: -440px -120px; } -.emoji-flag_as { background-position: -440px -140px; } -.emoji-flag_at { background-position: -440px -160px; } -.emoji-flag_au { background-position: -440px -180px; } -.emoji-flag_aw { background-position: -440px -200px; } -.emoji-flag_ax { background-position: -440px -220px; } -.emoji-flag_az { background-position: -440px -240px; } -.emoji-flag_ba { background-position: -440px -260px; } -.emoji-flag_bb { background-position: -440px -280px; } -.emoji-flag_bd { background-position: -440px -300px; } -.emoji-flag_be { background-position: -440px -320px; } -.emoji-flag_bf { background-position: -440px -340px; } -.emoji-flag_bg { background-position: -440px -360px; } -.emoji-flag_bh { background-position: -440px -380px; } -.emoji-flag_bi { background-position: -440px -400px; } -.emoji-flag_bj { background-position: -440px -420px; } -.emoji-flag_bl { background-position: 0 -440px; } -.emoji-flag_black { background-position: -20px -440px; } -.emoji-flag_bm { background-position: -40px -440px; } -.emoji-flag_bn { background-position: -60px -440px; } -.emoji-flag_bo { background-position: -80px -440px; } -.emoji-flag_bq { background-position: -100px -440px; } -.emoji-flag_br { background-position: -120px -440px; } -.emoji-flag_bs { background-position: -140px -440px; } -.emoji-flag_bt { background-position: -160px -440px; } -.emoji-flag_bv { background-position: -180px -440px; } -.emoji-flag_bw { background-position: -200px -440px; } -.emoji-flag_by { background-position: -220px -440px; } -.emoji-flag_bz { background-position: -240px -440px; } -.emoji-flag_ca { background-position: -260px -440px; } -.emoji-flag_cc { background-position: -280px -440px; } -.emoji-flag_cd { background-position: -300px -440px; } -.emoji-flag_cf { background-position: -320px -440px; } -.emoji-flag_cg { background-position: -340px -440px; } -.emoji-flag_ch { background-position: -360px -440px; } -.emoji-flag_ci { background-position: -380px -440px; } -.emoji-flag_ck { background-position: -400px -440px; } -.emoji-flag_cl { background-position: -420px -440px; } -.emoji-flag_cm { background-position: -440px -440px; } -.emoji-flag_cn { background-position: -460px 0; } -.emoji-flag_co { background-position: -460px -20px; } -.emoji-flag_cp { background-position: -460px -40px; } -.emoji-flag_cr { background-position: -460px -60px; } -.emoji-flag_cu { background-position: -460px -80px; } -.emoji-flag_cv { background-position: -460px -100px; } -.emoji-flag_cw { background-position: -460px -120px; } -.emoji-flag_cx { background-position: -460px -140px; } -.emoji-flag_cy { background-position: -460px -160px; } -.emoji-flag_cz { background-position: -460px -180px; } -.emoji-flag_de { background-position: -460px -200px; } -.emoji-flag_dg { background-position: -460px -220px; } -.emoji-flag_dj { background-position: -460px -240px; } -.emoji-flag_dk { background-position: -460px -260px; } -.emoji-flag_dm { background-position: -460px -280px; } -.emoji-flag_do { background-position: -460px -300px; } -.emoji-flag_dz { background-position: -460px -320px; } -.emoji-flag_ea { background-position: -460px -340px; } -.emoji-flag_ec { background-position: -460px -360px; } -.emoji-flag_ee { background-position: -460px -380px; } -.emoji-flag_eg { background-position: -460px -400px; } -.emoji-flag_eh { background-position: -460px -420px; } -.emoji-flag_er { background-position: -460px -440px; } -.emoji-flag_es { background-position: 0 -460px; } -.emoji-flag_et { background-position: -20px -460px; } -.emoji-flag_eu { background-position: -40px -460px; } -.emoji-flag_fi { background-position: -60px -460px; } -.emoji-flag_fj { background-position: -80px -460px; } -.emoji-flag_fk { background-position: -100px -460px; } -.emoji-flag_fm { background-position: -120px -460px; } -.emoji-flag_fo { background-position: -140px -460px; } -.emoji-flag_fr { background-position: -160px -460px; } -.emoji-flag_ga { background-position: -180px -460px; } -.emoji-flag_gb { background-position: -200px -460px; } -.emoji-flag_gd { background-position: -220px -460px; } -.emoji-flag_ge { background-position: -240px -460px; } -.emoji-flag_gf { background-position: -260px -460px; } -.emoji-flag_gg { background-position: -280px -460px; } -.emoji-flag_gh { background-position: -300px -460px; } -.emoji-flag_gi { background-position: -320px -460px; } -.emoji-flag_gl { background-position: -340px -460px; } -.emoji-flag_gm { background-position: -360px -460px; } -.emoji-flag_gn { background-position: -380px -460px; } -.emoji-flag_gp { background-position: -400px -460px; } -.emoji-flag_gq { background-position: -420px -460px; } -.emoji-flag_gr { background-position: -440px -460px; } -.emoji-flag_gs { background-position: -460px -460px; } -.emoji-flag_gt { background-position: -480px 0; } -.emoji-flag_gu { background-position: -480px -20px; } -.emoji-flag_gw { background-position: -480px -40px; } -.emoji-flag_gy { background-position: -480px -60px; } -.emoji-flag_hk { background-position: -480px -80px; } -.emoji-flag_hm { background-position: -480px -100px; } -.emoji-flag_hn { background-position: -480px -120px; } -.emoji-flag_hr { background-position: -480px -140px; } -.emoji-flag_ht { background-position: -480px -160px; } -.emoji-flag_hu { background-position: -480px -180px; } -.emoji-flag_ic { background-position: -480px -200px; } -.emoji-flag_id { background-position: -480px -220px; } -.emoji-flag_ie { background-position: -480px -240px; } -.emoji-flag_il { background-position: -480px -260px; } -.emoji-flag_im { background-position: -480px -280px; } -.emoji-flag_in { background-position: -480px -300px; } -.emoji-flag_io { background-position: -480px -320px; } -.emoji-flag_iq { background-position: -480px -340px; } -.emoji-flag_ir { background-position: -480px -360px; } -.emoji-flag_is { background-position: -480px -380px; } -.emoji-flag_it { background-position: -480px -400px; } -.emoji-flag_je { background-position: -480px -420px; } -.emoji-flag_jm { background-position: -480px -440px; } -.emoji-flag_jo { background-position: -480px -460px; } -.emoji-flag_jp { background-position: 0 -480px; } -.emoji-flag_ke { background-position: -20px -480px; } -.emoji-flag_kg { background-position: -40px -480px; } -.emoji-flag_kh { background-position: -60px -480px; } -.emoji-flag_ki { background-position: -80px -480px; } -.emoji-flag_km { background-position: -100px -480px; } -.emoji-flag_kn { background-position: -120px -480px; } -.emoji-flag_kp { background-position: -140px -480px; } -.emoji-flag_kr { background-position: -160px -480px; } -.emoji-flag_kw { background-position: -180px -480px; } -.emoji-flag_ky { background-position: -200px -480px; } -.emoji-flag_kz { background-position: -220px -480px; } -.emoji-flag_la { background-position: -240px -480px; } -.emoji-flag_lb { background-position: -260px -480px; } -.emoji-flag_lc { background-position: -280px -480px; } -.emoji-flag_li { background-position: -300px -480px; } -.emoji-flag_lk { background-position: -320px -480px; } -.emoji-flag_lr { background-position: -340px -480px; } -.emoji-flag_ls { background-position: -360px -480px; } -.emoji-flag_lt { background-position: -380px -480px; } -.emoji-flag_lu { background-position: -400px -480px; } -.emoji-flag_lv { background-position: -420px -480px; } -.emoji-flag_ly { background-position: -440px -480px; } -.emoji-flag_ma { background-position: -460px -480px; } -.emoji-flag_mc { background-position: -480px -480px; } -.emoji-flag_md { background-position: -500px 0; } -.emoji-flag_me { background-position: -500px -20px; } -.emoji-flag_mf { background-position: -500px -40px; } -.emoji-flag_mg { background-position: -500px -60px; } -.emoji-flag_mh { background-position: -500px -80px; } -.emoji-flag_mk { background-position: -500px -100px; } -.emoji-flag_ml { background-position: -500px -120px; } -.emoji-flag_mm { background-position: -500px -140px; } -.emoji-flag_mn { background-position: -500px -160px; } -.emoji-flag_mo { background-position: -500px -180px; } -.emoji-flag_mp { background-position: -500px -200px; } -.emoji-flag_mq { background-position: -500px -220px; } -.emoji-flag_mr { background-position: -500px -240px; } -.emoji-flag_ms { background-position: -500px -260px; } -.emoji-flag_mt { background-position: -500px -280px; } -.emoji-flag_mu { background-position: -500px -300px; } -.emoji-flag_mv { background-position: -500px -320px; } -.emoji-flag_mw { background-position: -500px -340px; } -.emoji-flag_mx { background-position: -500px -360px; } -.emoji-flag_my { background-position: -500px -380px; } -.emoji-flag_mz { background-position: -500px -400px; } -.emoji-flag_na { background-position: -500px -420px; } -.emoji-flag_nc { background-position: -500px -440px; } -.emoji-flag_ne { background-position: -500px -460px; } -.emoji-flag_nf { background-position: -500px -480px; } -.emoji-flag_ng { background-position: 0 -500px; } -.emoji-flag_ni { background-position: -20px -500px; } -.emoji-flag_nl { background-position: -40px -500px; } -.emoji-flag_no { background-position: -60px -500px; } -.emoji-flag_np { background-position: -80px -500px; } -.emoji-flag_nr { background-position: -100px -500px; } -.emoji-flag_nu { background-position: -120px -500px; } -.emoji-flag_nz { background-position: -140px -500px; } -.emoji-flag_om { background-position: -160px -500px; } -.emoji-flag_pa { background-position: -180px -500px; } -.emoji-flag_pe { background-position: -200px -500px; } -.emoji-flag_pf { background-position: -220px -500px; } -.emoji-flag_pg { background-position: -240px -500px; } -.emoji-flag_ph { background-position: -260px -500px; } -.emoji-flag_pk { background-position: -280px -500px; } -.emoji-flag_pl { background-position: -300px -500px; } -.emoji-flag_pm { background-position: -320px -500px; } -.emoji-flag_pn { background-position: -340px -500px; } -.emoji-flag_pr { background-position: -360px -500px; } -.emoji-flag_ps { background-position: -380px -500px; } -.emoji-flag_pt { background-position: -400px -500px; } -.emoji-flag_pw { background-position: -420px -500px; } -.emoji-flag_py { background-position: -440px -500px; } -.emoji-flag_qa { background-position: -460px -500px; } -.emoji-flag_re { background-position: -480px -500px; } -.emoji-flag_ro { background-position: -500px -500px; } -.emoji-flag_rs { background-position: -520px 0; } -.emoji-flag_ru { background-position: -520px -20px; } -.emoji-flag_rw { background-position: -520px -40px; } -.emoji-flag_sa { background-position: -520px -60px; } -.emoji-flag_sb { background-position: -520px -80px; } -.emoji-flag_sc { background-position: -520px -100px; } -.emoji-flag_sd { background-position: -520px -120px; } -.emoji-flag_se { background-position: -520px -140px; } -.emoji-flag_sg { background-position: -520px -160px; } -.emoji-flag_sh { background-position: -520px -180px; } -.emoji-flag_si { background-position: -520px -200px; } -.emoji-flag_sj { background-position: -520px -220px; } -.emoji-flag_sk { background-position: -520px -240px; } -.emoji-flag_sl { background-position: -520px -260px; } -.emoji-flag_sm { background-position: -520px -280px; } -.emoji-flag_sn { background-position: -520px -300px; } -.emoji-flag_so { background-position: -520px -320px; } -.emoji-flag_sr { background-position: -520px -340px; } -.emoji-flag_ss { background-position: -520px -360px; } -.emoji-flag_st { background-position: -520px -380px; } -.emoji-flag_sv { background-position: -520px -400px; } -.emoji-flag_sx { background-position: -520px -420px; } -.emoji-flag_sy { background-position: -520px -440px; } -.emoji-flag_sz { background-position: -520px -460px; } -.emoji-flag_ta { background-position: -520px -480px; } -.emoji-flag_tc { background-position: -520px -500px; } -.emoji-flag_td { background-position: 0 -520px; } -.emoji-flag_tf { background-position: -20px -520px; } -.emoji-flag_tg { background-position: -40px -520px; } -.emoji-flag_th { background-position: -60px -520px; } -.emoji-flag_tj { background-position: -80px -520px; } -.emoji-flag_tk { background-position: -100px -520px; } -.emoji-flag_tl { background-position: -120px -520px; } -.emoji-flag_tm { background-position: -140px -520px; } -.emoji-flag_tn { background-position: -160px -520px; } -.emoji-flag_to { background-position: -180px -520px; } -.emoji-flag_tr { background-position: -200px -520px; } -.emoji-flag_tt { background-position: -220px -520px; } -.emoji-flag_tv { background-position: -240px -520px; } -.emoji-flag_tw { background-position: -260px -520px; } -.emoji-flag_tz { background-position: -280px -520px; } -.emoji-flag_ua { background-position: -300px -520px; } -.emoji-flag_ug { background-position: -320px -520px; } -.emoji-flag_um { background-position: -340px -520px; } -.emoji-flag_us { background-position: -360px -520px; } -.emoji-flag_uy { background-position: -380px -520px; } -.emoji-flag_uz { background-position: -400px -520px; } -.emoji-flag_va { background-position: -420px -520px; } -.emoji-flag_vc { background-position: -440px -520px; } -.emoji-flag_ve { background-position: -460px -520px; } -.emoji-flag_vg { background-position: -480px -520px; } -.emoji-flag_vi { background-position: -500px -520px; } -.emoji-flag_vn { background-position: -520px -520px; } -.emoji-flag_vu { background-position: -540px 0; } -.emoji-flag_wf { background-position: -540px -20px; } -.emoji-flag_white { background-position: -540px -40px; } -.emoji-flag_ws { background-position: -540px -60px; } -.emoji-flag_xk { background-position: -540px -80px; } -.emoji-flag_ye { background-position: -540px -100px; } -.emoji-flag_yt { background-position: -540px -120px; } -.emoji-flag_za { background-position: -540px -140px; } -.emoji-flag_zm { background-position: -540px -160px; } -.emoji-flag_zw { background-position: -540px -180px; } -.emoji-flags { background-position: -540px -200px; } -.emoji-flashlight { background-position: -540px -220px; } -.emoji-fleur-de-lis { background-position: -540px -240px; } -.emoji-floppy_disk { background-position: -540px -260px; } -.emoji-flower_playing_cards { background-position: -540px -280px; } -.emoji-flushed { background-position: -540px -300px; } -.emoji-fog { background-position: -540px -320px; } -.emoji-foggy { background-position: -540px -340px; } -.emoji-football { background-position: -540px -360px; } -.emoji-footprints { background-position: -540px -380px; } -.emoji-fork_and_knife { background-position: -540px -400px; } -.emoji-fork_knife_plate { background-position: -540px -420px; } -.emoji-fountain { background-position: -540px -440px; } -.emoji-four { background-position: -540px -460px; } -.emoji-four_leaf_clover { background-position: -540px -480px; } -.emoji-fox { background-position: -540px -500px; } -.emoji-frame_photo { background-position: -540px -520px; } -.emoji-free { background-position: 0 -540px; } -.emoji-french_bread { background-position: -20px -540px; } -.emoji-fried_shrimp { background-position: -40px -540px; } -.emoji-fries { background-position: -60px -540px; } -.emoji-frog { background-position: -80px -540px; } -.emoji-frowning { background-position: -100px -540px; } -.emoji-frowning2 { background-position: -120px -540px; } -.emoji-fuelpump { background-position: -140px -540px; } -.emoji-full_moon { background-position: -160px -540px; } -.emoji-full_moon_with_face { background-position: -180px -540px; } -.emoji-game_die { background-position: -200px -540px; } -.emoji-gay_pride_flag { background-position: -220px -540px; } -.emoji-gear { background-position: -240px -540px; } -.emoji-gem { background-position: -260px -540px; } -.emoji-gemini { background-position: -280px -540px; } -.emoji-ghost { background-position: -300px -540px; } -.emoji-gift { background-position: -320px -540px; } -.emoji-gift_heart { background-position: -340px -540px; } -.emoji-girl { background-position: -360px -540px; } -.emoji-girl_tone1 { background-position: -380px -540px; } -.emoji-girl_tone2 { background-position: -400px -540px; } -.emoji-girl_tone3 { background-position: -420px -540px; } -.emoji-girl_tone4 { background-position: -440px -540px; } -.emoji-girl_tone5 { background-position: -460px -540px; } -.emoji-globe_with_meridians { background-position: -480px -540px; } -.emoji-goal { background-position: -500px -540px; } -.emoji-goat { background-position: -520px -540px; } -.emoji-golf { background-position: -540px -540px; } -.emoji-golfer { background-position: -560px 0; } -.emoji-gorilla { background-position: -560px -20px; } -.emoji-grapes { background-position: -560px -40px; } -.emoji-green_apple { background-position: -560px -60px; } -.emoji-green_book { background-position: -560px -80px; } -.emoji-green_heart { background-position: -560px -100px; } -.emoji-grey_exclamation { background-position: -560px -120px; } -.emoji-grey_question { background-position: -560px -140px; } -.emoji-grimacing { background-position: -560px -160px; } -.emoji-grin { background-position: -560px -180px; } -.emoji-grinning { background-position: -560px -200px; } -.emoji-guardsman { background-position: -560px -220px; } -.emoji-guardsman_tone1 { background-position: -560px -240px; } -.emoji-guardsman_tone2 { background-position: -560px -260px; } -.emoji-guardsman_tone3 { background-position: -560px -280px; } -.emoji-guardsman_tone4 { background-position: -560px -300px; } -.emoji-guardsman_tone5 { background-position: -560px -320px; } -.emoji-guitar { background-position: -560px -340px; } -.emoji-gun { background-position: -560px -360px; } -.emoji-haircut { background-position: -560px -380px; } -.emoji-haircut_tone1 { background-position: -560px -400px; } -.emoji-haircut_tone2 { background-position: -560px -420px; } -.emoji-haircut_tone3 { background-position: -560px -440px; } -.emoji-haircut_tone4 { background-position: -560px -460px; } -.emoji-haircut_tone5 { background-position: -560px -480px; } -.emoji-hamburger { background-position: -560px -500px; } -.emoji-hammer { background-position: -560px -520px; } -.emoji-hammer_pick { background-position: -560px -540px; } -.emoji-hamster { background-position: 0 -560px; } -.emoji-hand_splayed { background-position: -20px -560px; } -.emoji-hand_splayed_tone1 { background-position: -40px -560px; } -.emoji-hand_splayed_tone2 { background-position: -60px -560px; } -.emoji-hand_splayed_tone3 { background-position: -80px -560px; } -.emoji-hand_splayed_tone4 { background-position: -100px -560px; } -.emoji-hand_splayed_tone5 { background-position: -120px -560px; } -.emoji-handbag { background-position: -140px -560px; } -.emoji-handball { background-position: -160px -560px; } -.emoji-handball_tone1 { background-position: -180px -560px; } -.emoji-handball_tone2 { background-position: -200px -560px; } -.emoji-handball_tone3 { background-position: -220px -560px; } -.emoji-handball_tone4 { background-position: -240px -560px; } -.emoji-handball_tone5 { background-position: -260px -560px; } -.emoji-handshake { background-position: -280px -560px; } -.emoji-handshake_tone1 { background-position: -300px -560px; } -.emoji-handshake_tone2 { background-position: -320px -560px; } -.emoji-handshake_tone3 { background-position: -340px -560px; } -.emoji-handshake_tone4 { background-position: -360px -560px; } -.emoji-handshake_tone5 { background-position: -380px -560px; } -.emoji-hash { background-position: -400px -560px; } -.emoji-hatched_chick { background-position: -420px -560px; } -.emoji-hatching_chick { background-position: -440px -560px; } -.emoji-head_bandage { background-position: -460px -560px; } -.emoji-headphones { background-position: -480px -560px; } -.emoji-hear_no_evil { background-position: -500px -560px; } -.emoji-heart { background-position: -520px -560px; } -.emoji-heart_decoration { background-position: -540px -560px; } -.emoji-heart_exclamation { background-position: -560px -560px; } -.emoji-heart_eyes { background-position: -580px 0; } -.emoji-heart_eyes_cat { background-position: -580px -20px; } -.emoji-heartbeat { background-position: -580px -40px; } -.emoji-heartpulse { background-position: -580px -60px; } -.emoji-hearts { background-position: -580px -80px; } -.emoji-heavy_check_mark { background-position: -580px -100px; } -.emoji-heavy_division_sign { background-position: -580px -120px; } -.emoji-heavy_dollar_sign { background-position: -580px -140px; } -.emoji-heavy_minus_sign { background-position: -580px -160px; } -.emoji-heavy_multiplication_x { background-position: -580px -180px; } -.emoji-heavy_plus_sign { background-position: -580px -200px; } -.emoji-helicopter { background-position: -580px -220px; } -.emoji-helmet_with_cross { background-position: -580px -240px; } -.emoji-herb { background-position: -580px -260px; } -.emoji-hibiscus { background-position: -580px -280px; } -.emoji-high_brightness { background-position: -580px -300px; } -.emoji-high_heel { background-position: -580px -320px; } -.emoji-hockey { background-position: -580px -340px; } -.emoji-hole { background-position: -580px -360px; } -.emoji-homes { background-position: -580px -380px; } -.emoji-honey_pot { background-position: -580px -400px; } -.emoji-horse { background-position: -580px -420px; } -.emoji-horse_racing { background-position: -580px -440px; } -.emoji-horse_racing_tone1 { background-position: -580px -460px; } -.emoji-horse_racing_tone2 { background-position: -580px -480px; } -.emoji-horse_racing_tone3 { background-position: -580px -500px; } -.emoji-horse_racing_tone4 { background-position: -580px -520px; } -.emoji-horse_racing_tone5 { background-position: -580px -540px; } -.emoji-hospital { background-position: -580px -560px; } -.emoji-hot_pepper { background-position: 0 -580px; } -.emoji-hotdog { background-position: -20px -580px; } -.emoji-hotel { background-position: -40px -580px; } -.emoji-hotsprings { background-position: -60px -580px; } -.emoji-hourglass { background-position: -80px -580px; } -.emoji-hourglass_flowing_sand { background-position: -100px -580px; } -.emoji-house { background-position: -120px -580px; } -.emoji-house_abandoned { background-position: -140px -580px; } -.emoji-house_with_garden { background-position: -160px -580px; } -.emoji-hugging { background-position: -180px -580px; } -.emoji-hushed { background-position: -200px -580px; } -.emoji-ice_cream { background-position: -220px -580px; } -.emoji-ice_skate { background-position: -240px -580px; } -.emoji-icecream { background-position: -260px -580px; } -.emoji-id { background-position: -280px -580px; } -.emoji-ideograph_advantage { background-position: -300px -580px; } -.emoji-imp { background-position: -320px -580px; } -.emoji-inbox_tray { background-position: -340px -580px; } -.emoji-incoming_envelope { background-position: -360px -580px; } -.emoji-information_desk_person { background-position: -380px -580px; } -.emoji-information_desk_person_tone1 { background-position: -400px -580px; } -.emoji-information_desk_person_tone2 { background-position: -420px -580px; } -.emoji-information_desk_person_tone3 { background-position: -440px -580px; } -.emoji-information_desk_person_tone4 { background-position: -460px -580px; } -.emoji-information_desk_person_tone5 { background-position: -480px -580px; } -.emoji-information_source { background-position: -500px -580px; } -.emoji-innocent { background-position: -520px -580px; } -.emoji-interrobang { background-position: -540px -580px; } -.emoji-iphone { background-position: -560px -580px; } -.emoji-island { background-position: -580px -580px; } -.emoji-izakaya_lantern { background-position: -600px 0; } -.emoji-jack_o_lantern { background-position: -600px -20px; } -.emoji-japan { background-position: -600px -40px; } -.emoji-japanese_castle { background-position: -600px -60px; } -.emoji-japanese_goblin { background-position: -600px -80px; } -.emoji-japanese_ogre { background-position: -600px -100px; } -.emoji-jeans { background-position: -600px -120px; } -.emoji-joy { background-position: -600px -140px; } -.emoji-joy_cat { background-position: -600px -160px; } -.emoji-joystick { background-position: -600px -180px; } -.emoji-juggling { background-position: -600px -200px; } -.emoji-juggling_tone1 { background-position: -600px -220px; } -.emoji-juggling_tone2 { background-position: -600px -240px; } -.emoji-juggling_tone3 { background-position: -600px -260px; } -.emoji-juggling_tone4 { background-position: -600px -280px; } -.emoji-juggling_tone5 { background-position: -600px -300px; } -.emoji-kaaba { background-position: -600px -320px; } -.emoji-key { background-position: -600px -340px; } -.emoji-key2 { background-position: -600px -360px; } -.emoji-keyboard { background-position: -600px -380px; } -.emoji-kimono { background-position: -600px -400px; } -.emoji-kiss { background-position: -600px -420px; } -.emoji-kiss_mm { background-position: -600px -440px; } -.emoji-kiss_ww { background-position: -600px -460px; } -.emoji-kissing { background-position: -600px -480px; } -.emoji-kissing_cat { background-position: -600px -500px; } -.emoji-kissing_closed_eyes { background-position: -600px -520px; } -.emoji-kissing_heart { background-position: -600px -540px; } -.emoji-kissing_smiling_eyes { background-position: -600px -560px; } -.emoji-kiwi { background-position: -600px -580px; } -.emoji-knife { background-position: 0 -600px; } -.emoji-koala { background-position: -20px -600px; } -.emoji-koko { background-position: -40px -600px; } -.emoji-label { background-position: -60px -600px; } -.emoji-large_blue_circle { background-position: -80px -600px; } -.emoji-large_blue_diamond { background-position: -100px -600px; } -.emoji-large_orange_diamond { background-position: -120px -600px; } -.emoji-last_quarter_moon { background-position: -140px -600px; } -.emoji-last_quarter_moon_with_face { background-position: -160px -600px; } -.emoji-laughing { background-position: -180px -600px; } -.emoji-leaves { background-position: -200px -600px; } -.emoji-ledger { background-position: -220px -600px; } -.emoji-left_facing_fist { background-position: -240px -600px; } -.emoji-left_facing_fist_tone1 { background-position: -260px -600px; } -.emoji-left_facing_fist_tone2 { background-position: -280px -600px; } -.emoji-left_facing_fist_tone3 { background-position: -300px -600px; } -.emoji-left_facing_fist_tone4 { background-position: -320px -600px; } -.emoji-left_facing_fist_tone5 { background-position: -340px -600px; } -.emoji-left_luggage { background-position: -360px -600px; } -.emoji-left_right_arrow { background-position: -380px -600px; } -.emoji-leftwards_arrow_with_hook { background-position: -400px -600px; } -.emoji-lemon { background-position: -420px -600px; } -.emoji-leo { background-position: -440px -600px; } -.emoji-leopard { background-position: -460px -600px; } -.emoji-level_slider { background-position: -480px -600px; } -.emoji-levitate { background-position: -500px -600px; } -.emoji-libra { background-position: -520px -600px; } -.emoji-lifter { background-position: -540px -600px; } -.emoji-lifter_tone1 { background-position: -560px -600px; } -.emoji-lifter_tone2 { background-position: -580px -600px; } -.emoji-lifter_tone3 { background-position: -600px -600px; } -.emoji-lifter_tone4 { background-position: -620px 0; } -.emoji-lifter_tone5 { background-position: -620px -20px; } -.emoji-light_rail { background-position: -620px -40px; } -.emoji-link { background-position: -620px -60px; } -.emoji-lion_face { background-position: -620px -80px; } -.emoji-lips { background-position: -620px -100px; } -.emoji-lipstick { background-position: -620px -120px; } -.emoji-lizard { background-position: -620px -140px; } -.emoji-lock { background-position: -620px -160px; } -.emoji-lock_with_ink_pen { background-position: -620px -180px; } -.emoji-lollipop { background-position: -620px -200px; } -.emoji-loop { background-position: -620px -220px; } -.emoji-loud_sound { background-position: -620px -240px; } -.emoji-loudspeaker { background-position: -620px -260px; } -.emoji-love_hotel { background-position: -620px -280px; } -.emoji-love_letter { background-position: -620px -300px; } -.emoji-low_brightness { background-position: -620px -320px; } -.emoji-lying_face { background-position: -620px -340px; } -.emoji-m { background-position: -620px -360px; } -.emoji-mag { background-position: -620px -380px; } -.emoji-mag_right { background-position: -620px -400px; } -.emoji-mahjong { background-position: -620px -420px; } -.emoji-mailbox { background-position: -620px -440px; } -.emoji-mailbox_closed { background-position: -620px -460px; } -.emoji-mailbox_with_mail { background-position: -620px -480px; } -.emoji-mailbox_with_no_mail { background-position: -620px -500px; } -.emoji-man { background-position: -620px -520px; } -.emoji-man_dancing { background-position: -620px -540px; } -.emoji-man_dancing_tone1 { background-position: -620px -560px; } -.emoji-man_dancing_tone2 { background-position: -620px -580px; } -.emoji-man_dancing_tone3 { background-position: -620px -600px; } -.emoji-man_dancing_tone4 { background-position: 0 -620px; } -.emoji-man_dancing_tone5 { background-position: -20px -620px; } -.emoji-man_in_tuxedo { background-position: -40px -620px; } -.emoji-man_in_tuxedo_tone1 { background-position: -60px -620px; } -.emoji-man_in_tuxedo_tone2 { background-position: -80px -620px; } -.emoji-man_in_tuxedo_tone3 { background-position: -100px -620px; } -.emoji-man_in_tuxedo_tone4 { background-position: -120px -620px; } -.emoji-man_in_tuxedo_tone5 { background-position: -140px -620px; } -.emoji-man_tone1 { background-position: -160px -620px; } -.emoji-man_tone2 { background-position: -180px -620px; } -.emoji-man_tone3 { background-position: -200px -620px; } -.emoji-man_tone4 { background-position: -220px -620px; } -.emoji-man_tone5 { background-position: -240px -620px; } -.emoji-man_with_gua_pi_mao { background-position: -260px -620px; } -.emoji-man_with_gua_pi_mao_tone1 { background-position: -280px -620px; } -.emoji-man_with_gua_pi_mao_tone2 { background-position: -300px -620px; } -.emoji-man_with_gua_pi_mao_tone3 { background-position: -320px -620px; } -.emoji-man_with_gua_pi_mao_tone4 { background-position: -340px -620px; } -.emoji-man_with_gua_pi_mao_tone5 { background-position: -360px -620px; } -.emoji-man_with_turban { background-position: -380px -620px; } -.emoji-man_with_turban_tone1 { background-position: -400px -620px; } -.emoji-man_with_turban_tone2 { background-position: -420px -620px; } -.emoji-man_with_turban_tone3 { background-position: -440px -620px; } -.emoji-man_with_turban_tone4 { background-position: -460px -620px; } -.emoji-man_with_turban_tone5 { background-position: -480px -620px; } -.emoji-mans_shoe { background-position: -500px -620px; } -.emoji-map { background-position: -520px -620px; } -.emoji-maple_leaf { background-position: -540px -620px; } -.emoji-martial_arts_uniform { background-position: -560px -620px; } -.emoji-mask { background-position: -580px -620px; } -.emoji-massage { background-position: -600px -620px; } -.emoji-massage_tone1 { background-position: -620px -620px; } -.emoji-massage_tone2 { background-position: -640px 0; } -.emoji-massage_tone3 { background-position: -640px -20px; } -.emoji-massage_tone4 { background-position: -640px -40px; } -.emoji-massage_tone5 { background-position: -640px -60px; } -.emoji-meat_on_bone { background-position: -640px -80px; } -.emoji-medal { background-position: -640px -100px; } -.emoji-mega { background-position: -640px -120px; } -.emoji-melon { background-position: -640px -140px; } -.emoji-menorah { background-position: -640px -160px; } -.emoji-mens { background-position: -640px -180px; } -.emoji-metal { background-position: -640px -200px; } -.emoji-metal_tone1 { background-position: -640px -220px; } -.emoji-metal_tone2 { background-position: -640px -240px; } -.emoji-metal_tone3 { background-position: -640px -260px; } -.emoji-metal_tone4 { background-position: -640px -280px; } -.emoji-metal_tone5 { background-position: -640px -300px; } -.emoji-metro { background-position: -640px -320px; } -.emoji-microphone { background-position: -640px -340px; } -.emoji-microphone2 { background-position: -640px -360px; } -.emoji-microscope { background-position: -640px -380px; } -.emoji-middle_finger { background-position: -640px -400px; } -.emoji-middle_finger_tone1 { background-position: -640px -420px; } -.emoji-middle_finger_tone2 { background-position: -640px -440px; } -.emoji-middle_finger_tone3 { background-position: -640px -460px; } -.emoji-middle_finger_tone4 { background-position: -640px -480px; } -.emoji-middle_finger_tone5 { background-position: -640px -500px; } -.emoji-military_medal { background-position: -640px -520px; } -.emoji-milk { background-position: -640px -540px; } -.emoji-milky_way { background-position: -640px -560px; } -.emoji-minibus { background-position: -640px -580px; } -.emoji-minidisc { background-position: -640px -600px; } -.emoji-mobile_phone_off { background-position: -640px -620px; } -.emoji-money_mouth { background-position: 0 -640px; } -.emoji-money_with_wings { background-position: -20px -640px; } -.emoji-moneybag { background-position: -40px -640px; } -.emoji-monkey { background-position: -60px -640px; } -.emoji-monkey_face { background-position: -80px -640px; } -.emoji-monorail { background-position: -100px -640px; } -.emoji-mortar_board { background-position: -120px -640px; } -.emoji-mosque { background-position: -140px -640px; } -.emoji-motor_scooter { background-position: -160px -640px; } -.emoji-motorboat { background-position: -180px -640px; } -.emoji-motorcycle { background-position: -200px -640px; } -.emoji-motorway { background-position: -220px -640px; } -.emoji-mount_fuji { background-position: -240px -640px; } -.emoji-mountain { background-position: -260px -640px; } -.emoji-mountain_bicyclist { background-position: -280px -640px; } -.emoji-mountain_bicyclist_tone1 { background-position: -300px -640px; } -.emoji-mountain_bicyclist_tone2 { background-position: -320px -640px; } -.emoji-mountain_bicyclist_tone3 { background-position: -340px -640px; } -.emoji-mountain_bicyclist_tone4 { background-position: -360px -640px; } -.emoji-mountain_bicyclist_tone5 { background-position: -380px -640px; } -.emoji-mountain_cableway { background-position: -400px -640px; } -.emoji-mountain_railway { background-position: -420px -640px; } -.emoji-mountain_snow { background-position: -440px -640px; } -.emoji-mouse { background-position: -460px -640px; } -.emoji-mouse2 { background-position: -480px -640px; } -.emoji-mouse_three_button { background-position: -500px -640px; } -.emoji-movie_camera { background-position: -520px -640px; } -.emoji-moyai { background-position: -540px -640px; } -.emoji-mrs_claus { background-position: -560px -640px; } -.emoji-mrs_claus_tone1 { background-position: -580px -640px; } -.emoji-mrs_claus_tone2 { background-position: -600px -640px; } -.emoji-mrs_claus_tone3 { background-position: -620px -640px; } -.emoji-mrs_claus_tone4 { background-position: -640px -640px; } -.emoji-mrs_claus_tone5 { background-position: -660px 0; } -.emoji-muscle { background-position: -660px -20px; } -.emoji-muscle_tone1 { background-position: -660px -40px; } -.emoji-muscle_tone2 { background-position: -660px -60px; } -.emoji-muscle_tone3 { background-position: -660px -80px; } -.emoji-muscle_tone4 { background-position: -660px -100px; } -.emoji-muscle_tone5 { background-position: -660px -120px; } -.emoji-mushroom { background-position: -660px -140px; } -.emoji-musical_keyboard { background-position: -660px -160px; } -.emoji-musical_note { background-position: -660px -180px; } -.emoji-musical_score { background-position: -660px -200px; } -.emoji-mute { background-position: -660px -220px; } -.emoji-nail_care { background-position: -660px -240px; } -.emoji-nail_care_tone1 { background-position: -660px -260px; } -.emoji-nail_care_tone2 { background-position: -660px -280px; } -.emoji-nail_care_tone3 { background-position: -660px -300px; } -.emoji-nail_care_tone4 { background-position: -660px -320px; } -.emoji-nail_care_tone5 { background-position: -660px -340px; } -.emoji-name_badge { background-position: -660px -360px; } -.emoji-nauseated_face { background-position: -660px -380px; } -.emoji-necktie { background-position: -660px -400px; } -.emoji-negative_squared_cross_mark { background-position: -660px -420px; } -.emoji-nerd { background-position: -660px -440px; } -.emoji-neutral_face { background-position: -660px -460px; } -.emoji-new { background-position: -660px -480px; } -.emoji-new_moon { background-position: -660px -500px; } -.emoji-new_moon_with_face { background-position: -660px -520px; } -.emoji-newspaper { background-position: -660px -540px; } -.emoji-newspaper2 { background-position: -660px -560px; } -.emoji-ng { background-position: -660px -580px; } -.emoji-night_with_stars { background-position: -660px -600px; } -.emoji-nine { background-position: -660px -620px; } -.emoji-no_bell { background-position: -660px -640px; } -.emoji-no_bicycles { background-position: 0 -660px; } -.emoji-no_entry { background-position: -20px -660px; } -.emoji-no_entry_sign { background-position: -40px -660px; } -.emoji-no_good { background-position: -60px -660px; } -.emoji-no_good_tone1 { background-position: -80px -660px; } -.emoji-no_good_tone2 { background-position: -100px -660px; } -.emoji-no_good_tone3 { background-position: -120px -660px; } -.emoji-no_good_tone4 { background-position: -140px -660px; } -.emoji-no_good_tone5 { background-position: -160px -660px; } -.emoji-no_mobile_phones { background-position: -180px -660px; } -.emoji-no_mouth { background-position: -200px -660px; } -.emoji-no_pedestrians { background-position: -220px -660px; } -.emoji-no_smoking { background-position: -240px -660px; } -.emoji-non-potable_water { background-position: -260px -660px; } -.emoji-nose { background-position: -280px -660px; } -.emoji-nose_tone1 { background-position: -300px -660px; } -.emoji-nose_tone2 { background-position: -320px -660px; } -.emoji-nose_tone3 { background-position: -340px -660px; } -.emoji-nose_tone4 { background-position: -360px -660px; } -.emoji-nose_tone5 { background-position: -380px -660px; } -.emoji-notebook { background-position: -400px -660px; } -.emoji-notebook_with_decorative_cover { background-position: -420px -660px; } -.emoji-notepad_spiral { background-position: -440px -660px; } -.emoji-notes { background-position: -460px -660px; } -.emoji-nut_and_bolt { background-position: -480px -660px; } -.emoji-o { background-position: -500px -660px; } -.emoji-o2 { background-position: -520px -660px; } -.emoji-ocean { background-position: -540px -660px; } -.emoji-octagonal_sign { background-position: -560px -660px; } -.emoji-octopus { background-position: -580px -660px; } -.emoji-oden { background-position: -600px -660px; } -.emoji-office { background-position: -620px -660px; } -.emoji-oil { background-position: -640px -660px; } -.emoji-ok { background-position: -660px -660px; } -.emoji-ok_hand { background-position: -680px 0; } -.emoji-ok_hand_tone1 { background-position: -680px -20px; } -.emoji-ok_hand_tone2 { background-position: -680px -40px; } -.emoji-ok_hand_tone3 { background-position: -680px -60px; } -.emoji-ok_hand_tone4 { background-position: -680px -80px; } -.emoji-ok_hand_tone5 { background-position: -680px -100px; } -.emoji-ok_woman { background-position: -680px -120px; } -.emoji-ok_woman_tone1 { background-position: -680px -140px; } -.emoji-ok_woman_tone2 { background-position: -680px -160px; } -.emoji-ok_woman_tone3 { background-position: -680px -180px; } -.emoji-ok_woman_tone4 { background-position: -680px -200px; } -.emoji-ok_woman_tone5 { background-position: -680px -220px; } -.emoji-older_man { background-position: -680px -240px; } -.emoji-older_man_tone1 { background-position: -680px -260px; } -.emoji-older_man_tone2 { background-position: -680px -280px; } -.emoji-older_man_tone3 { background-position: -680px -300px; } -.emoji-older_man_tone4 { background-position: -680px -320px; } -.emoji-older_man_tone5 { background-position: -680px -340px; } -.emoji-older_woman { background-position: -680px -360px; } -.emoji-older_woman_tone1 { background-position: -680px -380px; } -.emoji-older_woman_tone2 { background-position: -680px -400px; } -.emoji-older_woman_tone3 { background-position: -680px -420px; } -.emoji-older_woman_tone4 { background-position: -680px -440px; } -.emoji-older_woman_tone5 { background-position: -680px -460px; } -.emoji-om_symbol { background-position: -680px -480px; } -.emoji-on { background-position: -680px -500px; } -.emoji-oncoming_automobile { background-position: -680px -520px; } -.emoji-oncoming_bus { background-position: -680px -540px; } -.emoji-oncoming_police_car { background-position: -680px -560px; } -.emoji-oncoming_taxi { background-position: -680px -580px; } -.emoji-one { background-position: -680px -600px; } -.emoji-open_file_folder { background-position: -680px -620px; } -.emoji-open_hands { background-position: -680px -640px; } -.emoji-open_hands_tone1 { background-position: -680px -660px; } -.emoji-open_hands_tone2 { background-position: 0 -680px; } -.emoji-open_hands_tone3 { background-position: -20px -680px; } -.emoji-open_hands_tone4 { background-position: -40px -680px; } -.emoji-open_hands_tone5 { background-position: -60px -680px; } -.emoji-open_mouth { background-position: -80px -680px; } -.emoji-ophiuchus { background-position: -100px -680px; } -.emoji-orange_book { background-position: -120px -680px; } -.emoji-orthodox_cross { background-position: -140px -680px; } -.emoji-outbox_tray { background-position: -160px -680px; } -.emoji-owl { background-position: -180px -680px; } -.emoji-ox { background-position: -200px -680px; } -.emoji-package { background-position: -220px -680px; } -.emoji-page_facing_up { background-position: -240px -680px; } -.emoji-page_with_curl { background-position: -260px -680px; } -.emoji-pager { background-position: -280px -680px; } -.emoji-paintbrush { background-position: -300px -680px; } -.emoji-palm_tree { background-position: -320px -680px; } -.emoji-pancakes { background-position: -340px -680px; } -.emoji-panda_face { background-position: -360px -680px; } -.emoji-paperclip { background-position: -380px -680px; } -.emoji-paperclips { background-position: -400px -680px; } -.emoji-park { background-position: -420px -680px; } -.emoji-parking { background-position: -440px -680px; } -.emoji-part_alternation_mark { background-position: -460px -680px; } -.emoji-partly_sunny { background-position: -480px -680px; } -.emoji-passport_control { background-position: -500px -680px; } -.emoji-pause_button { background-position: -520px -680px; } -.emoji-peace { background-position: -540px -680px; } -.emoji-peach { background-position: -560px -680px; } -.emoji-peanuts { background-position: -580px -680px; } -.emoji-pear { background-position: -600px -680px; } -.emoji-pen_ballpoint { background-position: -620px -680px; } -.emoji-pen_fountain { background-position: -640px -680px; } -.emoji-pencil { background-position: -660px -680px; } -.emoji-pencil2 { background-position: -680px -680px; } -.emoji-penguin { background-position: -700px 0; } -.emoji-pensive { background-position: -700px -20px; } -.emoji-performing_arts { background-position: -700px -40px; } -.emoji-persevere { background-position: -700px -60px; } -.emoji-person_frowning { background-position: -700px -80px; } -.emoji-person_frowning_tone1 { background-position: -700px -100px; } -.emoji-person_frowning_tone2 { background-position: -700px -120px; } -.emoji-person_frowning_tone3 { background-position: -700px -140px; } -.emoji-person_frowning_tone4 { background-position: -700px -160px; } -.emoji-person_frowning_tone5 { background-position: -700px -180px; } -.emoji-person_with_blond_hair { background-position: -700px -200px; } -.emoji-person_with_blond_hair_tone1 { background-position: -700px -220px; } -.emoji-person_with_blond_hair_tone2 { background-position: -700px -240px; } -.emoji-person_with_blond_hair_tone3 { background-position: -700px -260px; } -.emoji-person_with_blond_hair_tone4 { background-position: -700px -280px; } -.emoji-person_with_blond_hair_tone5 { background-position: -700px -300px; } -.emoji-person_with_pouting_face { background-position: -700px -320px; } -.emoji-person_with_pouting_face_tone1 { background-position: -700px -340px; } -.emoji-person_with_pouting_face_tone2 { background-position: -700px -360px; } -.emoji-person_with_pouting_face_tone3 { background-position: -700px -380px; } -.emoji-person_with_pouting_face_tone4 { background-position: -700px -400px; } -.emoji-person_with_pouting_face_tone5 { background-position: -700px -420px; } -.emoji-pick { background-position: -700px -440px; } -.emoji-pig { background-position: -700px -460px; } -.emoji-pig2 { background-position: -700px -480px; } -.emoji-pig_nose { background-position: -700px -500px; } -.emoji-pill { background-position: -700px -520px; } -.emoji-pineapple { background-position: -700px -540px; } -.emoji-ping_pong { background-position: -700px -560px; } -.emoji-pisces { background-position: -700px -580px; } -.emoji-pizza { background-position: -700px -600px; } -.emoji-place_of_worship { background-position: -700px -620px; } -.emoji-play_pause { background-position: -700px -640px; } -.emoji-point_down { background-position: -700px -660px; } -.emoji-point_down_tone1 { background-position: -700px -680px; } -.emoji-point_down_tone2 { background-position: 0 -700px; } -.emoji-point_down_tone3 { background-position: -20px -700px; } -.emoji-point_down_tone4 { background-position: -40px -700px; } -.emoji-point_down_tone5 { background-position: -60px -700px; } -.emoji-point_left { background-position: -80px -700px; } -.emoji-point_left_tone1 { background-position: -100px -700px; } -.emoji-point_left_tone2 { background-position: -120px -700px; } -.emoji-point_left_tone3 { background-position: -140px -700px; } -.emoji-point_left_tone4 { background-position: -160px -700px; } -.emoji-point_left_tone5 { background-position: -180px -700px; } -.emoji-point_right { background-position: -200px -700px; } -.emoji-point_right_tone1 { background-position: -220px -700px; } -.emoji-point_right_tone2 { background-position: -240px -700px; } -.emoji-point_right_tone3 { background-position: -260px -700px; } -.emoji-point_right_tone4 { background-position: -280px -700px; } -.emoji-point_right_tone5 { background-position: -300px -700px; } -.emoji-point_up { background-position: -320px -700px; } -.emoji-point_up_2 { background-position: -340px -700px; } -.emoji-point_up_2_tone1 { background-position: -360px -700px; } -.emoji-point_up_2_tone2 { background-position: -380px -700px; } -.emoji-point_up_2_tone3 { background-position: -400px -700px; } -.emoji-point_up_2_tone4 { background-position: -420px -700px; } -.emoji-point_up_2_tone5 { background-position: -440px -700px; } -.emoji-point_up_tone1 { background-position: -460px -700px; } -.emoji-point_up_tone2 { background-position: -480px -700px; } -.emoji-point_up_tone3 { background-position: -500px -700px; } -.emoji-point_up_tone4 { background-position: -520px -700px; } -.emoji-point_up_tone5 { background-position: -540px -700px; } -.emoji-police_car { background-position: -560px -700px; } -.emoji-poodle { background-position: -580px -700px; } -.emoji-poop { background-position: -600px -700px; } -.emoji-popcorn { background-position: -620px -700px; } -.emoji-post_office { background-position: -640px -700px; } -.emoji-postal_horn { background-position: -660px -700px; } -.emoji-postbox { background-position: -680px -700px; } -.emoji-potable_water { background-position: -700px -700px; } -.emoji-potato { background-position: -720px 0; } -.emoji-pouch { background-position: -720px -20px; } -.emoji-poultry_leg { background-position: -720px -40px; } -.emoji-pound { background-position: -720px -60px; } -.emoji-pouting_cat { background-position: -720px -80px; } -.emoji-pray { background-position: -720px -100px; } -.emoji-pray_tone1 { background-position: -720px -120px; } -.emoji-pray_tone2 { background-position: -720px -140px; } -.emoji-pray_tone3 { background-position: -720px -160px; } -.emoji-pray_tone4 { background-position: -720px -180px; } -.emoji-pray_tone5 { background-position: -720px -200px; } -.emoji-prayer_beads { background-position: -720px -220px; } -.emoji-pregnant_woman { background-position: -720px -240px; } -.emoji-pregnant_woman_tone1 { background-position: -720px -260px; } -.emoji-pregnant_woman_tone2 { background-position: -720px -280px; } -.emoji-pregnant_woman_tone3 { background-position: -720px -300px; } -.emoji-pregnant_woman_tone4 { background-position: -720px -320px; } -.emoji-pregnant_woman_tone5 { background-position: -720px -340px; } -.emoji-prince { background-position: -720px -360px; } -.emoji-prince_tone1 { background-position: -720px -380px; } -.emoji-prince_tone2 { background-position: -720px -400px; } -.emoji-prince_tone3 { background-position: -720px -420px; } -.emoji-prince_tone4 { background-position: -720px -440px; } -.emoji-prince_tone5 { background-position: -720px -460px; } -.emoji-princess { background-position: -720px -480px; } -.emoji-princess_tone1 { background-position: -720px -500px; } -.emoji-princess_tone2 { background-position: -720px -520px; } -.emoji-princess_tone3 { background-position: -720px -540px; } -.emoji-princess_tone4 { background-position: -720px -560px; } -.emoji-princess_tone5 { background-position: -720px -580px; } -.emoji-printer { background-position: -720px -600px; } -.emoji-projector { background-position: -720px -620px; } -.emoji-punch { background-position: -720px -640px; } -.emoji-punch_tone1 { background-position: -720px -660px; } -.emoji-punch_tone2 { background-position: -720px -680px; } -.emoji-punch_tone3 { background-position: -720px -700px; } -.emoji-punch_tone4 { background-position: 0 -720px; } -.emoji-punch_tone5 { background-position: -20px -720px; } -.emoji-purple_heart { background-position: -40px -720px; } -.emoji-purse { background-position: -60px -720px; } -.emoji-pushpin { background-position: -80px -720px; } -.emoji-put_litter_in_its_place { background-position: -100px -720px; } -.emoji-question { background-position: -120px -720px; } -.emoji-rabbit { background-position: -140px -720px; } -.emoji-rabbit2 { background-position: -160px -720px; } -.emoji-race_car { background-position: -180px -720px; } -.emoji-racehorse { background-position: -200px -720px; } -.emoji-radio { background-position: -220px -720px; } -.emoji-radio_button { background-position: -240px -720px; } -.emoji-radioactive { background-position: -260px -720px; } -.emoji-rage { background-position: -280px -720px; } -.emoji-railway_car { background-position: -300px -720px; } -.emoji-railway_track { background-position: -320px -720px; } -.emoji-rainbow { background-position: -340px -720px; } -.emoji-raised_back_of_hand { background-position: -360px -720px; } -.emoji-raised_back_of_hand_tone1 { background-position: -380px -720px; } -.emoji-raised_back_of_hand_tone2 { background-position: -400px -720px; } -.emoji-raised_back_of_hand_tone3 { background-position: -420px -720px; } -.emoji-raised_back_of_hand_tone4 { background-position: -440px -720px; } -.emoji-raised_back_of_hand_tone5 { background-position: -460px -720px; } -.emoji-raised_hand { background-position: -480px -720px; } -.emoji-raised_hand_tone1 { background-position: -500px -720px; } -.emoji-raised_hand_tone2 { background-position: -520px -720px; } -.emoji-raised_hand_tone3 { background-position: -540px -720px; } -.emoji-raised_hand_tone4 { background-position: -560px -720px; } -.emoji-raised_hand_tone5 { background-position: -580px -720px; } -.emoji-raised_hands { background-position: -600px -720px; } -.emoji-raised_hands_tone1 { background-position: -620px -720px; } -.emoji-raised_hands_tone2 { background-position: -640px -720px; } -.emoji-raised_hands_tone3 { background-position: -660px -720px; } -.emoji-raised_hands_tone4 { background-position: -680px -720px; } -.emoji-raised_hands_tone5 { background-position: -700px -720px; } -.emoji-raising_hand { background-position: -720px -720px; } -.emoji-raising_hand_tone1 { background-position: -740px 0; } -.emoji-raising_hand_tone2 { background-position: -740px -20px; } -.emoji-raising_hand_tone3 { background-position: -740px -40px; } -.emoji-raising_hand_tone4 { background-position: -740px -60px; } -.emoji-raising_hand_tone5 { background-position: -740px -80px; } -.emoji-ram { background-position: -740px -100px; } -.emoji-ramen { background-position: -740px -120px; } -.emoji-rat { background-position: -740px -140px; } -.emoji-record_button { background-position: -740px -160px; } -.emoji-recycle { background-position: -740px -180px; } -.emoji-red_car { background-position: -740px -200px; } -.emoji-red_circle { background-position: -740px -220px; } -.emoji-registered { background-position: -740px -240px; } -.emoji-relaxed { background-position: -740px -260px; } -.emoji-relieved { background-position: -740px -280px; } -.emoji-reminder_ribbon { background-position: -740px -300px; } -.emoji-repeat { background-position: -740px -320px; } -.emoji-repeat_one { background-position: -740px -340px; } -.emoji-restroom { background-position: -740px -360px; } -.emoji-revolving_hearts { background-position: -740px -380px; } -.emoji-rewind { background-position: -740px -400px; } -.emoji-rhino { background-position: -740px -420px; } -.emoji-ribbon { background-position: -740px -440px; } -.emoji-rice { background-position: -740px -460px; } -.emoji-rice_ball { background-position: -740px -480px; } -.emoji-rice_cracker { background-position: -740px -500px; } -.emoji-rice_scene { background-position: -740px -520px; } -.emoji-right_facing_fist { background-position: -740px -540px; } -.emoji-right_facing_fist_tone1 { background-position: -740px -560px; } -.emoji-right_facing_fist_tone2 { background-position: -740px -580px; } -.emoji-right_facing_fist_tone3 { background-position: -740px -600px; } -.emoji-right_facing_fist_tone4 { background-position: -740px -620px; } -.emoji-right_facing_fist_tone5 { background-position: -740px -640px; } -.emoji-ring { background-position: -740px -660px; } -.emoji-robot { background-position: -740px -680px; } -.emoji-rocket { background-position: -740px -700px; } -.emoji-rofl { background-position: -740px -720px; } -.emoji-roller_coaster { background-position: 0 -740px; } -.emoji-rolling_eyes { background-position: -20px -740px; } -.emoji-rooster { background-position: -40px -740px; } -.emoji-rose { background-position: -60px -740px; } -.emoji-rosette { background-position: -80px -740px; } -.emoji-rotating_light { background-position: -100px -740px; } -.emoji-round_pushpin { background-position: -120px -740px; } -.emoji-rowboat { background-position: -140px -740px; } -.emoji-rowboat_tone1 { background-position: -160px -740px; } -.emoji-rowboat_tone2 { background-position: -180px -740px; } -.emoji-rowboat_tone3 { background-position: -200px -740px; } -.emoji-rowboat_tone4 { background-position: -220px -740px; } -.emoji-rowboat_tone5 { background-position: -240px -740px; } -.emoji-rugby_football { background-position: -260px -740px; } -.emoji-runner { background-position: -280px -740px; } -.emoji-runner_tone1 { background-position: -300px -740px; } -.emoji-runner_tone2 { background-position: -320px -740px; } -.emoji-runner_tone3 { background-position: -340px -740px; } -.emoji-runner_tone4 { background-position: -360px -740px; } -.emoji-runner_tone5 { background-position: -380px -740px; } -.emoji-running_shirt_with_sash { background-position: -400px -740px; } -.emoji-sa { background-position: -420px -740px; } -.emoji-sagittarius { background-position: -440px -740px; } -.emoji-sailboat { background-position: -460px -740px; } -.emoji-sake { background-position: -480px -740px; } -.emoji-salad { background-position: -500px -740px; } -.emoji-sandal { background-position: -520px -740px; } -.emoji-santa { background-position: -540px -740px; } -.emoji-santa_tone1 { background-position: -560px -740px; } -.emoji-santa_tone2 { background-position: -580px -740px; } -.emoji-santa_tone3 { background-position: -600px -740px; } -.emoji-santa_tone4 { background-position: -620px -740px; } -.emoji-santa_tone5 { background-position: -640px -740px; } -.emoji-satellite { background-position: -660px -740px; } -.emoji-satellite_orbital { background-position: -680px -740px; } -.emoji-saxophone { background-position: -700px -740px; } -.emoji-scales { background-position: -720px -740px; } -.emoji-school { background-position: -740px -740px; } -.emoji-school_satchel { background-position: -760px 0; } -.emoji-scissors { background-position: -760px -20px; } -.emoji-scooter { background-position: -760px -40px; } -.emoji-scorpion { background-position: -760px -60px; } -.emoji-scorpius { background-position: -760px -80px; } -.emoji-scream { background-position: -760px -100px; } -.emoji-scream_cat { background-position: -760px -120px; } -.emoji-scroll { background-position: -760px -140px; } -.emoji-seat { background-position: -760px -160px; } -.emoji-second_place { background-position: -760px -180px; } -.emoji-secret { background-position: -760px -200px; } -.emoji-see_no_evil { background-position: -760px -220px; } -.emoji-seedling { background-position: -760px -240px; } -.emoji-selfie { background-position: -760px -260px; } -.emoji-selfie_tone1 { background-position: -760px -280px; } -.emoji-selfie_tone2 { background-position: -760px -300px; } -.emoji-selfie_tone3 { background-position: -760px -320px; } -.emoji-selfie_tone4 { background-position: -760px -340px; } -.emoji-selfie_tone5 { background-position: -760px -360px; } -.emoji-seven { background-position: -760px -380px; } -.emoji-shallow_pan_of_food { background-position: -760px -400px; } -.emoji-shamrock { background-position: -760px -420px; } -.emoji-shark { background-position: -760px -440px; } -.emoji-shaved_ice { background-position: -760px -460px; } -.emoji-sheep { background-position: -760px -480px; } -.emoji-shell { background-position: -760px -500px; } -.emoji-shield { background-position: -760px -520px; } -.emoji-shinto_shrine { background-position: -760px -540px; } -.emoji-ship { background-position: -760px -560px; } -.emoji-shirt { background-position: -760px -580px; } -.emoji-shopping_bags { background-position: -760px -600px; } -.emoji-shopping_cart { background-position: -760px -620px; } -.emoji-shower { background-position: -760px -640px; } -.emoji-shrimp { background-position: -760px -660px; } -.emoji-shrug { background-position: -760px -680px; } -.emoji-shrug_tone1 { background-position: -760px -700px; } -.emoji-shrug_tone2 { background-position: -760px -720px; } -.emoji-shrug_tone3 { background-position: -760px -740px; } -.emoji-shrug_tone4 { background-position: 0 -760px; } -.emoji-shrug_tone5 { background-position: -20px -760px; } -.emoji-signal_strength { background-position: -40px -760px; } -.emoji-six { background-position: -60px -760px; } -.emoji-six_pointed_star { background-position: -80px -760px; } -.emoji-ski { background-position: -100px -760px; } -.emoji-skier { background-position: -120px -760px; } -.emoji-skull { background-position: -140px -760px; } -.emoji-skull_crossbones { background-position: -160px -760px; } -.emoji-sleeping { background-position: -180px -760px; } -.emoji-sleeping_accommodation { background-position: -200px -760px; } -.emoji-sleepy { background-position: -220px -760px; } -.emoji-slight_frown { background-position: -240px -760px; } -.emoji-slight_smile { background-position: -260px -760px; } -.emoji-slot_machine { background-position: -280px -760px; } -.emoji-small_blue_diamond { background-position: -300px -760px; } -.emoji-small_orange_diamond { background-position: -320px -760px; } -.emoji-small_red_triangle { background-position: -340px -760px; } -.emoji-small_red_triangle_down { background-position: -360px -760px; } -.emoji-smile { background-position: -380px -760px; } -.emoji-smile_cat { background-position: -400px -760px; } -.emoji-smiley { background-position: -420px -760px; } -.emoji-smiley_cat { background-position: -440px -760px; } -.emoji-smiling_imp { background-position: -460px -760px; } -.emoji-smirk { background-position: -480px -760px; } -.emoji-smirk_cat { background-position: -500px -760px; } -.emoji-smoking { background-position: -520px -760px; } -.emoji-snail { background-position: -540px -760px; } -.emoji-snake { background-position: -560px -760px; } -.emoji-sneezing_face { background-position: -580px -760px; } -.emoji-snowboarder { background-position: -600px -760px; } -.emoji-snowflake { background-position: -620px -760px; } -.emoji-snowman { background-position: -640px -760px; } -.emoji-snowman2 { background-position: -660px -760px; } -.emoji-sob { background-position: -680px -760px; } -.emoji-soccer { background-position: -700px -760px; } -.emoji-soon { background-position: -720px -760px; } -.emoji-sos { background-position: -740px -760px; } -.emoji-sound { background-position: -760px -760px; } -.emoji-space_invader { background-position: -780px 0; } -.emoji-spades { background-position: -780px -20px; } -.emoji-spaghetti { background-position: -780px -40px; } -.emoji-sparkle { background-position: -780px -60px; } -.emoji-sparkler { background-position: -780px -80px; } -.emoji-sparkles { background-position: -780px -100px; } -.emoji-sparkling_heart { background-position: -780px -120px; } -.emoji-speak_no_evil { background-position: -780px -140px; } -.emoji-speaker { background-position: -780px -160px; } -.emoji-speaking_head { background-position: -780px -180px; } -.emoji-speech_balloon { background-position: -780px -200px; } -.emoji-speech_left { background-position: -780px -220px; } -.emoji-speedboat { background-position: -780px -240px; } -.emoji-spider { background-position: -780px -260px; } -.emoji-spider_web { background-position: -780px -280px; } -.emoji-spoon { background-position: -780px -300px; } -.emoji-spy { background-position: -780px -320px; } -.emoji-spy_tone1 { background-position: -780px -340px; } -.emoji-spy_tone2 { background-position: -780px -360px; } -.emoji-spy_tone3 { background-position: -780px -380px; } -.emoji-spy_tone4 { background-position: -780px -400px; } -.emoji-spy_tone5 { background-position: -780px -420px; } -.emoji-squid { background-position: -780px -440px; } -.emoji-stadium { background-position: -780px -460px; } -.emoji-star { background-position: -780px -480px; } -.emoji-star2 { background-position: -780px -500px; } -.emoji-star_and_crescent { background-position: -780px -520px; } -.emoji-star_of_david { background-position: -780px -540px; } -.emoji-stars { background-position: -780px -560px; } -.emoji-station { background-position: -780px -580px; } -.emoji-statue_of_liberty { background-position: -780px -600px; } -.emoji-steam_locomotive { background-position: -780px -620px; } -.emoji-stew { background-position: -780px -640px; } -.emoji-stop_button { background-position: -780px -660px; } -.emoji-stopwatch { background-position: -780px -680px; } -.emoji-straight_ruler { background-position: -780px -700px; } -.emoji-strawberry { background-position: -780px -720px; } -.emoji-stuck_out_tongue { background-position: -780px -740px; } -.emoji-stuck_out_tongue_closed_eyes { background-position: -780px -760px; } -.emoji-stuck_out_tongue_winking_eye { background-position: 0 -780px; } -.emoji-stuffed_flatbread { background-position: -20px -780px; } -.emoji-sun_with_face { background-position: -40px -780px; } -.emoji-sunflower { background-position: -60px -780px; } -.emoji-sunglasses { background-position: -80px -780px; } -.emoji-sunny { background-position: -100px -780px; } -.emoji-sunrise { background-position: -120px -780px; } -.emoji-sunrise_over_mountains { background-position: -140px -780px; } -.emoji-surfer { background-position: -160px -780px; } -.emoji-surfer_tone1 { background-position: -180px -780px; } -.emoji-surfer_tone2 { background-position: -200px -780px; } -.emoji-surfer_tone3 { background-position: -220px -780px; } -.emoji-surfer_tone4 { background-position: -240px -780px; } -.emoji-surfer_tone5 { background-position: -260px -780px; } -.emoji-sushi { background-position: -280px -780px; } -.emoji-suspension_railway { background-position: -300px -780px; } -.emoji-sweat { background-position: -320px -780px; } -.emoji-sweat_drops { background-position: -340px -780px; } -.emoji-sweat_smile { background-position: -360px -780px; } -.emoji-sweet_potato { background-position: -380px -780px; } -.emoji-swimmer { background-position: -400px -780px; } -.emoji-swimmer_tone1 { background-position: -420px -780px; } -.emoji-swimmer_tone2 { background-position: -440px -780px; } -.emoji-swimmer_tone3 { background-position: -460px -780px; } -.emoji-swimmer_tone4 { background-position: -480px -780px; } -.emoji-swimmer_tone5 { background-position: -500px -780px; } -.emoji-symbols { background-position: -520px -780px; } -.emoji-synagogue { background-position: -540px -780px; } -.emoji-syringe { background-position: -560px -780px; } -.emoji-taco { background-position: -580px -780px; } -.emoji-tada { background-position: -600px -780px; } -.emoji-tanabata_tree { background-position: -620px -780px; } -.emoji-tangerine { background-position: -640px -780px; } -.emoji-taurus { background-position: -660px -780px; } -.emoji-taxi { background-position: -680px -780px; } -.emoji-tea { background-position: -700px -780px; } -.emoji-telephone { background-position: -720px -780px; } -.emoji-telephone_receiver { background-position: -740px -780px; } -.emoji-telescope { background-position: -760px -780px; } -.emoji-ten { background-position: -780px -780px; } -.emoji-tennis { background-position: -800px 0; } -.emoji-tent { background-position: -800px -20px; } -.emoji-thermometer { background-position: -800px -40px; } -.emoji-thermometer_face { background-position: -800px -60px; } -.emoji-thinking { background-position: -800px -80px; } -.emoji-third_place { background-position: -800px -100px; } -.emoji-thought_balloon { background-position: -800px -120px; } -.emoji-three { background-position: -800px -140px; } -.emoji-thumbsdown { background-position: -800px -160px; } -.emoji-thumbsdown_tone1 { background-position: -800px -180px; } -.emoji-thumbsdown_tone2 { background-position: -800px -200px; } -.emoji-thumbsdown_tone3 { background-position: -800px -220px; } -.emoji-thumbsdown_tone4 { background-position: -800px -240px; } -.emoji-thumbsdown_tone5 { background-position: -800px -260px; } -.emoji-thumbsup { background-position: -800px -280px; } -.emoji-thumbsup_tone1 { background-position: -800px -300px; } -.emoji-thumbsup_tone2 { background-position: -800px -320px; } -.emoji-thumbsup_tone3 { background-position: -800px -340px; } -.emoji-thumbsup_tone4 { background-position: -800px -360px; } -.emoji-thumbsup_tone5 { background-position: -800px -380px; } -.emoji-thunder_cloud_rain { background-position: -800px -400px; } -.emoji-ticket { background-position: -800px -420px; } -.emoji-tickets { background-position: -800px -440px; } -.emoji-tiger { background-position: -800px -460px; } -.emoji-tiger2 { background-position: -800px -480px; } -.emoji-timer { background-position: -800px -500px; } -.emoji-tired_face { background-position: -800px -520px; } -.emoji-tm { background-position: -800px -540px; } -.emoji-toilet { background-position: -800px -560px; } -.emoji-tokyo_tower { background-position: -800px -580px; } -.emoji-tomato { background-position: -800px -600px; } -.emoji-tone1 { background-position: -800px -620px; } -.emoji-tone2 { background-position: -800px -640px; } -.emoji-tone3 { background-position: -800px -660px; } -.emoji-tone4 { background-position: -800px -680px; } -.emoji-tone5 { background-position: -800px -700px; } -.emoji-tongue { background-position: -800px -720px; } -.emoji-tools { background-position: -800px -740px; } -.emoji-top { background-position: -800px -760px; } -.emoji-tophat { background-position: -800px -780px; } -.emoji-track_next { background-position: 0 -800px; } -.emoji-track_previous { background-position: -20px -800px; } -.emoji-trackball { background-position: -40px -800px; } -.emoji-tractor { background-position: -60px -800px; } -.emoji-traffic_light { background-position: -80px -800px; } -.emoji-train { background-position: -100px -800px; } -.emoji-train2 { background-position: -120px -800px; } -.emoji-tram { background-position: -140px -800px; } -.emoji-triangular_flag_on_post { background-position: -160px -800px; } -.emoji-triangular_ruler { background-position: -180px -800px; } -.emoji-trident { background-position: -200px -800px; } -.emoji-triumph { background-position: -220px -800px; } -.emoji-trolleybus { background-position: -240px -800px; } -.emoji-trophy { background-position: -260px -800px; } -.emoji-tropical_drink { background-position: -280px -800px; } -.emoji-tropical_fish { background-position: -300px -800px; } -.emoji-truck { background-position: -320px -800px; } -.emoji-trumpet { background-position: -340px -800px; } -.emoji-tulip { background-position: -360px -800px; } -.emoji-tumbler_glass { background-position: -380px -800px; } -.emoji-turkey { background-position: -400px -800px; } -.emoji-turtle { background-position: -420px -800px; } -.emoji-tv { background-position: -440px -800px; } -.emoji-twisted_rightwards_arrows { background-position: -460px -800px; } -.emoji-two { background-position: -480px -800px; } -.emoji-two_hearts { background-position: -500px -800px; } -.emoji-two_men_holding_hands { background-position: -520px -800px; } -.emoji-two_women_holding_hands { background-position: -540px -800px; } -.emoji-u5272 { background-position: -560px -800px; } -.emoji-u5408 { background-position: -580px -800px; } -.emoji-u55b6 { background-position: -600px -800px; } -.emoji-u6307 { background-position: -620px -800px; } -.emoji-u6708 { background-position: -640px -800px; } -.emoji-u6709 { background-position: -660px -800px; } -.emoji-u6e80 { background-position: -680px -800px; } -.emoji-u7121 { background-position: -700px -800px; } -.emoji-u7533 { background-position: -720px -800px; } -.emoji-u7981 { background-position: -740px -800px; } -.emoji-u7a7a { background-position: -760px -800px; } -.emoji-umbrella { background-position: -780px -800px; } -.emoji-umbrella2 { background-position: -800px -800px; } -.emoji-unamused { background-position: -820px 0; } -.emoji-underage { background-position: -820px -20px; } -.emoji-unicorn { background-position: -820px -40px; } -.emoji-unlock { background-position: -820px -60px; } -.emoji-up { background-position: -820px -80px; } -.emoji-upside_down { background-position: -820px -100px; } -.emoji-urn { background-position: -820px -120px; } -.emoji-v { background-position: -820px -140px; } -.emoji-v_tone1 { background-position: -820px -160px; } -.emoji-v_tone2 { background-position: -820px -180px; } -.emoji-v_tone3 { background-position: -820px -200px; } -.emoji-v_tone4 { background-position: -820px -220px; } -.emoji-v_tone5 { background-position: -820px -240px; } -.emoji-vertical_traffic_light { background-position: -820px -260px; } -.emoji-vhs { background-position: -820px -280px; } -.emoji-vibration_mode { background-position: -820px -300px; } -.emoji-video_camera { background-position: -820px -320px; } -.emoji-video_game { background-position: -820px -340px; } -.emoji-violin { background-position: -820px -360px; } -.emoji-virgo { background-position: -820px -380px; } -.emoji-volcano { background-position: -820px -400px; } -.emoji-volleyball { background-position: -820px -420px; } -.emoji-vs { background-position: -820px -440px; } -.emoji-vulcan { background-position: -820px -460px; } -.emoji-vulcan_tone1 { background-position: -820px -480px; } -.emoji-vulcan_tone2 { background-position: -820px -500px; } -.emoji-vulcan_tone3 { background-position: -820px -520px; } -.emoji-vulcan_tone4 { background-position: -820px -540px; } -.emoji-vulcan_tone5 { background-position: -820px -560px; } -.emoji-walking { background-position: -820px -580px; } -.emoji-walking_tone1 { background-position: -820px -600px; } -.emoji-walking_tone2 { background-position: -820px -620px; } -.emoji-walking_tone3 { background-position: -820px -640px; } -.emoji-walking_tone4 { background-position: -820px -660px; } -.emoji-walking_tone5 { background-position: -820px -680px; } -.emoji-waning_crescent_moon { background-position: -820px -700px; } -.emoji-waning_gibbous_moon { background-position: -820px -720px; } -.emoji-warning { background-position: -820px -740px; } -.emoji-wastebasket { background-position: -820px -760px; } -.emoji-watch { background-position: -820px -780px; } -.emoji-water_buffalo { background-position: -820px -800px; } -.emoji-water_polo { background-position: 0 -820px; } -.emoji-water_polo_tone1 { background-position: -20px -820px; } -.emoji-water_polo_tone2 { background-position: -40px -820px; } -.emoji-water_polo_tone3 { background-position: -60px -820px; } -.emoji-water_polo_tone4 { background-position: -80px -820px; } -.emoji-water_polo_tone5 { background-position: -100px -820px; } -.emoji-watermelon { background-position: -120px -820px; } -.emoji-wave { background-position: -140px -820px; } -.emoji-wave_tone1 { background-position: -160px -820px; } -.emoji-wave_tone2 { background-position: -180px -820px; } -.emoji-wave_tone3 { background-position: -200px -820px; } -.emoji-wave_tone4 { background-position: -220px -820px; } -.emoji-wave_tone5 { background-position: -240px -820px; } -.emoji-wavy_dash { background-position: -260px -820px; } -.emoji-waxing_crescent_moon { background-position: -280px -820px; } -.emoji-waxing_gibbous_moon { background-position: -300px -820px; } -.emoji-wc { background-position: -320px -820px; } -.emoji-weary { background-position: -340px -820px; } -.emoji-wedding { background-position: -360px -820px; } -.emoji-whale { background-position: -380px -820px; } -.emoji-whale2 { background-position: -400px -820px; } -.emoji-wheel_of_dharma { background-position: -420px -820px; } -.emoji-wheelchair { background-position: -440px -820px; } -.emoji-white_check_mark { background-position: -460px -820px; } -.emoji-white_circle { background-position: -480px -820px; } -.emoji-white_flower { background-position: -500px -820px; } -.emoji-white_large_square { background-position: -520px -820px; } -.emoji-white_medium_small_square { background-position: -540px -820px; } -.emoji-white_medium_square { background-position: -560px -820px; } -.emoji-white_small_square { background-position: -580px -820px; } -.emoji-white_square_button { background-position: -600px -820px; } -.emoji-white_sun_cloud { background-position: -620px -820px; } -.emoji-white_sun_rain_cloud { background-position: -640px -820px; } -.emoji-white_sun_small_cloud { background-position: -660px -820px; } -.emoji-wilted_rose { background-position: -680px -820px; } -.emoji-wind_blowing_face { background-position: -700px -820px; } -.emoji-wind_chime { background-position: -720px -820px; } -.emoji-wine_glass { background-position: -740px -820px; } -.emoji-wink { background-position: -760px -820px; } -.emoji-wolf { background-position: -780px -820px; } -.emoji-woman { background-position: -800px -820px; } -.emoji-woman_tone1 { background-position: -820px -820px; } -.emoji-woman_tone2 { background-position: -840px 0; } -.emoji-woman_tone3 { background-position: -840px -20px; } -.emoji-woman_tone4 { background-position: -840px -40px; } -.emoji-woman_tone5 { background-position: -840px -60px; } -.emoji-womans_clothes { background-position: -840px -80px; } -.emoji-womans_hat { background-position: -840px -100px; } -.emoji-womens { background-position: -840px -120px; } -.emoji-worried { background-position: -840px -140px; } -.emoji-wrench { background-position: -840px -160px; } -.emoji-wrestlers { background-position: -840px -180px; } -.emoji-wrestlers_tone1 { background-position: -840px -200px; } -.emoji-wrestlers_tone2 { background-position: -840px -220px; } -.emoji-wrestlers_tone3 { background-position: -840px -240px; } -.emoji-wrestlers_tone4 { background-position: -840px -260px; } -.emoji-wrestlers_tone5 { background-position: -840px -280px; } -.emoji-writing_hand { background-position: -840px -300px; } -.emoji-writing_hand_tone1 { background-position: -840px -320px; } -.emoji-writing_hand_tone2 { background-position: -840px -340px; } -.emoji-writing_hand_tone3 { background-position: -840px -360px; } -.emoji-writing_hand_tone4 { background-position: -840px -380px; } -.emoji-writing_hand_tone5 { background-position: -840px -400px; } -.emoji-x { background-position: -840px -420px; } -.emoji-yellow_heart { background-position: -840px -440px; } -.emoji-yen { background-position: -840px -460px; } -.emoji-yin_yang { background-position: -840px -480px; } -.emoji-yum { background-position: -840px -500px; } -.emoji-zap { background-position: -840px -520px; } -.emoji-zero { background-position: -840px -540px; } -.emoji-zipper_mouth { background-position: -840px -560px; } -.emoji-100 { background-position: -840px -580px; } - -.emoji-icon { - background-image: image-url('emoji.png'); - background-repeat: no-repeat; - color: transparent; - text-indent: -99em; - height: 20px; - width: 20px; - - @media only screen and (-webkit-min-device-pixel-ratio: 2), - only screen and (min--moz-device-pixel-ratio: 2), - only screen and (-o-min-device-pixel-ratio: 2/1), - only screen and (min-device-pixel-ratio: 2), - only screen and (min-resolution: 192dpi), - only screen and (min-resolution: 2dppx) { - background-image: image-url('emoji@2x.png'); - background-size: 860px 840px; - } -} diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index 62a0fba3da3..ab3cceceae9 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -39,35 +39,10 @@ svg { fill: currentColor; - &.s8 { - @include svg-size(8px); - } - - &.s12 { - @include svg-size(12px); - } - - &.s16 { - @include svg-size(16px); - } - - &.s18 { - @include svg-size(18px); - } - - &.s24 { - @include svg-size(24px); - } - - &.s32 { - @include svg-size(32px); - } - - &.s48 { - @include svg-size(48px); - } - - &.s72 { - @include svg-size(72px); + $svg-sizes: 8 12 16 18 24 32 48 72; + @each $svg-size in $svg-sizes { + &.s#{$svg-size} { + @include svg-size(#{$svg-size}px); + } } } diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index 938f5f49c09..7b5d1c2cf8b 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -107,6 +107,16 @@ padding-top: 10px; } +.referenced-commands { + background: $blue-50; + padding: $gl-padding-8 $gl-padding; + border-radius: $border-radius-default; + + p { + margin: 0; + } +} + .md-preview-holder { min-height: 167px; padding: 10px 0; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 8c44ebc85ef..3d28df455bb 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -212,6 +212,7 @@ $tooltip-font-size: 12px; /* * Padding */ +$gl-padding-24: 24px; $gl-padding: 16px; $gl-padding-8: 8px; $gl-padding-4: 4px; diff --git a/app/assets/stylesheets/pages/repo.scss.orig b/app/assets/stylesheets/pages/repo.scss.orig deleted file mode 100644 index 57b995adb64..00000000000 --- a/app/assets/stylesheets/pages/repo.scss.orig +++ /dev/null @@ -1,786 +0,0 @@ -.project-refs-form, -.project-refs-target-form { - display: inline-block; -} - -.fade-enter, -.fade-leave-to { - opacity: 0; -} - -.commit-message { - @include str-truncated(250px); -} - -.editable-mode { - display: inline-block; -} - -.ide-view { - display: flex; - height: calc(100vh - #{$header-height}); - margin-top: 40px; - color: $almost-black; - border-top: 1px solid $white-dark; - border-bottom: 1px solid $white-dark; - - &.is-collapsed { - .ide-file-list { - max-width: 250px; - } - } - - .file-status-icon { - width: 10px; - height: 10px; - } -} - -.ide-file-list { - flex: 1; - - .file { - cursor: pointer; - - &.file-open { - background: $white-normal; - } - - .ide-file-name { - flex: 1; - white-space: nowrap; - text-overflow: ellipsis; - - svg { - vertical-align: middle; - margin-right: 2px; - } - - .loading-container { - margin-right: 4px; - display: inline-block; - } - } - - .ide-file-changed-icon { - margin-left: auto; - } - - .ide-new-btn { - display: none; - margin-bottom: -4px; - margin-right: -8px; - } - - &:hover { - .ide-new-btn { - display: block; - } - } - - &.folder { - svg { - fill: $gl-text-color-secondary; - } - } - } - - a { - color: $gl-text-color; - } - - th { - position: sticky; - top: 0; - } -} - -.file-name, -.file-col-commit-message { - display: flex; - overflow: visible; - padding: 6px 12px; -} - -.multi-file-loading-container { - margin-top: 10px; - padding: 10px; - - .animation-container { - background: $gray-light; - - div { - background: $gray-light; - } - } -} - -.multi-file-table-col-commit-message { - white-space: nowrap; - width: 50%; -} - -.multi-file-edit-pane { - display: flex; - flex-direction: column; - flex: 1; - border-left: 1px solid $white-dark; - overflow: hidden; -} - -.multi-file-tabs { - display: flex; - background-color: $white-normal; - box-shadow: inset 0 -1px $white-dark; - - > ul { - display: flex; - overflow-x: auto; - } - - li { - position: relative; - } - - .dropdown { - display: flex; - margin-left: auto; - margin-bottom: 1px; - padding: 0 $grid-size; - border-left: 1px solid $white-dark; - background-color: $white-light; - - &.shadow { - box-shadow: 0 0 10px $dropdown-shadow-color; - } - - .btn { - margin-top: auto; - margin-bottom: auto; - } - } -} - -.multi-file-tab { - @include str-truncated(150px); - padding: ($gl-padding / 2) ($gl-padding + 12) ($gl-padding / 2) $gl-padding; - background-color: $gray-normal; - border-right: 1px solid $white-dark; - border-bottom: 1px solid $white-dark; - cursor: pointer; - - svg { - vertical-align: middle; - } - - &.active { - background-color: $white-light; - border-bottom-color: $white-light; - } -} - -.multi-file-tab-close { - position: absolute; - right: 8px; - top: 50%; - width: 16px; - height: 16px; - padding: 0; - background: none; - border: 0; - border-radius: $border-radius-default; - color: $theme-gray-900; - transform: translateY(-50%); - - svg { - position: relative; - top: -1px; - } - - &:hover { - background-color: $theme-gray-200; - } - - &:focus { - background-color: $blue-500; - color: $white-light; - outline: 0; - - svg { - fill: currentColor; - } - } -} - -.multi-file-edit-pane-content { - flex: 1; - height: 0; -} - -.blob-editor-container { - flex: 1; - height: 0; - display: flex; - flex-direction: column; - justify-content: center; - - .vertical-center { - min-height: auto; - } - - .monaco-editor .lines-content .cigr { - display: none; - } - - .monaco-diff-editor.vs { - .editor.modified { - box-shadow: none; - } - - .diagonal-fill { - display: none !important; - } - - .diffOverview { - background-color: $white-light; - border-left: 1px solid $white-dark; - cursor: ns-resize; - } - - .diffViewport { - display: none; - } - - .char-insert { - background-color: $line-added-dark; - } - - .char-delete { - background-color: $line-removed-dark; - } - - .line-numbers { - color: $black-transparent; - } - - .view-overlays { - .line-insert { - background-color: $line-added; - } - - .line-delete { - background-color: $line-removed; - } - } - - .margin { - background-color: $gray-light; - border-right: 1px solid $white-normal; - - .line-insert { - border-right: 1px solid $line-added-dark; - } - - .line-delete { - border-right: 1px solid $line-removed-dark; - } - } - - .margin-view-overlays .insert-sign, - .margin-view-overlays .delete-sign { - opacity: 0.4; - } - - .cursors-layer { - display: none; - } - } -} - -.multi-file-editor-holder { - height: 100%; -} - -.multi-file-editor-btn-group { - padding: $gl-bar-padding $gl-padding; - border-top: 1px solid $white-dark; - border-bottom: 1px solid $white-dark; - background: $white-light; -} - -.ide-status-bar { - padding: $gl-bar-padding $gl-padding; - background: $white-light; - display: flex; - justify-content: space-between; - - svg { - vertical-align: middle; - } -} - -// Not great, but this is to deal with our current output -.multi-file-preview-holder { - height: 100%; - overflow: scroll; - - .file-content.code { - display: flex; - - i { - margin-left: -10px; - } - } - - .line-numbers { - min-width: 50px; - } - - .file-content, - .line-numbers, - .blob-content, - .code { - min-height: 100%; - } -} - -.file-content.blob-no-preview { - a { - margin-left: auto; - margin-right: auto; - } -} - -.multi-file-commit-panel { - display: flex; - position: relative; - flex-direction: column; - width: 340px; - padding: 0; - background-color: $gray-light; - padding-right: 3px; - - .projects-sidebar { - display: flex; - flex-direction: column; - - .context-header { - width: auto; - margin-right: 0; - } - } - - .multi-file-commit-panel-inner { - display: flex; - flex: 1; - flex-direction: column; - } - - .multi-file-commit-panel-inner-scroll { - display: flex; - flex: 1; - flex-direction: column; - overflow: auto; - } - - &.is-collapsed { - width: 60px; - - .multi-file-commit-list { - padding-top: $gl-padding; - overflow: hidden; - } - - .multi-file-context-bar-icon { - align-items: center; - - svg { - float: none; - margin: 0; - } - } - } - - .branch-container { - border-left: 4px solid $indigo-700; - margin-bottom: $gl-bar-padding; - } - - .branch-header { - background: $white-dark; - display: flex; - } - - .branch-header-title { - flex: 1; - padding: $grid-size $gl-padding; - color: $indigo-700; - font-weight: $gl-font-weight-bold; - - svg { - vertical-align: middle; - } - } - - .branch-header-btns { - padding: $gl-vert-padding $gl-padding; - } - - .left-collapse-btn { - display: none; - background: $gray-light; - text-align: left; - border-top: 1px solid $white-dark; - - svg { - vertical-align: middle; - } - } -} - -.multi-file-context-bar-icon { - padding: 10px; - - svg { - margin-right: 10px; - float: left; - } -} - -.multi-file-commit-panel-section { - display: flex; - flex-direction: column; - flex: 1; -} - -.multi-file-commit-empty-state-container { - align-items: center; - justify-content: center; -} - -.multi-file-commit-panel-header { - display: flex; - align-items: center; - margin-bottom: 12px; - border-bottom: 1px solid $white-dark; - padding: $gl-btn-padding 0; - - &.is-collapsed { - border-bottom: 1px solid $white-dark; - - svg { - margin-left: auto; - margin-right: auto; - } - - .multi-file-commit-panel-collapse-btn { - margin-right: auto; - margin-left: auto; - border-left: 0; - } - } -} - -.multi-file-commit-panel-header-title { - display: flex; - flex: 1; - padding: 0 $gl-btn-padding; - - svg { - margin-right: $gl-btn-padding; - } -} - -.multi-file-commit-panel-collapse-btn { - border-left: 1px solid $white-dark; -} - -.multi-file-commit-list { - flex: 1; - overflow: auto; - padding: $gl-padding 0; - min-height: 60px; -} - -.multi-file-commit-list-item { - display: flex; - padding: 0; - align-items: center; - - .multi-file-discard-btn { - display: none; - margin-left: auto; - color: $gl-link-color; - padding: 0 2px; - - &:focus, - &:hover { - text-decoration: underline; - } - } - - &:hover { - background: $white-normal; - - .multi-file-discard-btn { - display: block; - } - } -} - -.multi-file-addition { - fill: $green-500; -} - -.multi-file-modified { - fill: $orange-500; -} - -.multi-file-commit-list-collapsed { - display: flex; - flex-direction: column; - - > svg { - margin-left: auto; - margin-right: auto; - } - - .file-status-icon { - width: 10px; - height: 10px; - margin-left: 3px; - } -} - -.multi-file-commit-list-path { - padding: $grid-size / 2; - padding-left: $gl-padding; - background: none; - border: 0; - text-align: left; - width: 100%; - min-width: 0; - - svg { - min-width: 16px; - vertical-align: middle; - display: inline-block; - } - - &:hover, - &:focus { - outline: 0; - } -} - -.multi-file-commit-list-file-path { - @include str-truncated(100%); - - &:hover { - text-decoration: underline; - } - - &:active { - text-decoration: none; - } -} - -.multi-file-commit-form { - padding: $gl-padding; - border-top: 1px solid $white-dark; - - .btn { - font-size: $gl-font-size; - } -} - -.multi-file-commit-message.form-control { - height: 160px; - resize: none; -} - -.dirty-diff { - // !important need to override monaco inline style - width: 4px !important; - left: 0 !important; - - &-modified { - background-color: $blue-500; - } - - &-added { - background-color: $green-600; - } - - &-removed { - height: 0 !important; - width: 0 !important; - bottom: -2px; - border-style: solid; - border-width: 5px; - border-color: transparent transparent transparent $red-500; - - &::before { - content: ''; - position: absolute; - left: 0; - top: 0; - width: 100px; - height: 1px; - background-color: rgba($red-500, 0.5); - } - } -} - -.ide-loading { - display: flex; - height: 100vh; - align-items: center; - justify-content: center; -} - -.ide-empty-state { - display: flex; - height: 100vh; - align-items: center; - justify-content: center; -} - -.ide-new-btn { - .dropdown-toggle svg { - margin-top: -2px; - margin-bottom: 2px; - } - - .dropdown-menu { - left: auto; - right: 0; - - label { - font-weight: $gl-font-weight-normal; - padding: 5px 8px; - margin-bottom: 0; - } - } -} - -.ide { - overflow: hidden; - - &.nav-only { - .flash-container { - margin-top: $header-height; - margin-bottom: 0; - } - - .alert-wrapper .flash-container .flash-alert:last-child, - .alert-wrapper .flash-container .flash-notice:last-child { - margin-bottom: 0; - } - - .content-wrapper { - margin-top: $header-height; - padding-bottom: 0; - } - - &.flash-shown { - .content-wrapper { - margin-top: 0; - } - - .ide-view { - height: calc(100vh - #{$header-height + $flash-height}); - } - } - - .projects-sidebar { - .multi-file-commit-panel-inner-scroll { - flex: 1; - } - } - } -} - -.with-performance-bar .ide.nav-only { - .flash-container { - margin-top: #{$header-height + $performance-bar-height}; - } - - .content-wrapper { - margin-top: #{$header-height + $performance-bar-height}; - padding-bottom: 0; - } - - .ide-view { - height: calc(100vh - #{$header-height + $performance-bar-height}); - } - - &.flash-shown { - .content-wrapper { - margin-top: 0; - } - - .ide-view { - height: calc( - 100vh - #{$header-height + $performance-bar-height + $flash-height} - ); - } - } -} - -.dragHandle { - position: absolute; - top: 0; - bottom: 0; - width: 3px; - background-color: $white-dark; - - &.dragright { - right: 0; - } - - &.dragleft { - left: 0; - } -} - -.ide-commit-radios { - label { - font-weight: normal; - } - - .help-block { - margin-top: 0; - line-height: 0; - } -} - -.ide-commit-new-branch { - margin-left: 25px; -} - -.ide-external-links { - p { - margin: 0; - } -} - -.ide-sidebar-link { - padding: $gl-padding-8 $gl-padding; - background: $indigo-700; - color: $white-light; - text-decoration: none; - display: flex; - align-items: center; - - &:focus, - &:hover { - color: $white-light; - text-decoration: underline; - background: $indigo-500; - } - - &:active { - background: $indigo-800; - } -} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0fdd4d2cb47..8ad13a82f89 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -110,7 +110,8 @@ class ApplicationController < ActionController::Base def log_exception(exception) Raven.capture_exception(exception) if sentry_enabled? - application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace + backtrace_cleaner = Gitlab.rails5? ? env["action_dispatch.backtrace_cleaner"] : env + application_trace = ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception).application_trace application_trace.map! { |t| " #{t}\n" } logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}" end diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 34228cf0b82..ca1b80a36a0 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -57,7 +57,7 @@ module IssuableCollections out_of_range = @issuables.current_page > total_pages # rubocop:disable Gitlab/ModuleWithInstanceVariables if out_of_range - redirect_to(url_for(params.merge(page: total_pages, only_path: true))) + redirect_to(url_for(safe_params.merge(page: total_pages, only_path: true))) end out_of_range diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index 9f3bb60b4cc..62213561898 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -33,6 +33,6 @@ class Groups::ApplicationController < ApplicationController def build_canonical_path(group) params[:group_id] = group.to_param - url_for(params) + url_for(safe_params) end end diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb new file mode 100644 index 00000000000..f0cdc228366 --- /dev/null +++ b/app/controllers/profiles/active_sessions_controller.rb @@ -0,0 +1,14 @@ +class Profiles::ActiveSessionsController < Profiles::ApplicationController + def index + @sessions = ActiveSession.list(current_user) + end + + def destroy + ActiveSession.destroy(current_user, params[:id]) + + respond_to do |format| + format.html { redirect_to profile_active_sessions_url, status: 302 } + format.js { head :ok } + end + end +end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 032bb2267e7..5ab6d103c89 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -25,7 +25,7 @@ class Projects::ApplicationController < ApplicationController params[:namespace_id] = project.namespace.to_param params[:project_id] = project.to_param - url_for(params) + url_for(safe_params) end def repository diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb index ebde0df1f7b..43d8867a536 100644 --- a/app/controllers/projects/lfs_storage_controller.rb +++ b/app/controllers/projects/lfs_storage_controller.rb @@ -77,8 +77,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController def link_to_project!(object) if object && !object.projects.exists?(storage_project.id) - object.projects << storage_project - object.save! + object.lfs_objects_projects.create!(project: storage_project) end end end diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 4a377fefc62..81129456ad8 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -83,13 +83,6 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap render layout: false end - def update_branches - @target_project = selected_target_project - @target_branches = @target_project ? @target_project.repository.branch_names : [] - - render layout: false - end - private def build_merge_request diff --git a/app/helpers/active_sessions_helper.rb b/app/helpers/active_sessions_helper.rb new file mode 100644 index 00000000000..97b6dac67c5 --- /dev/null +++ b/app/helpers/active_sessions_helper.rb @@ -0,0 +1,23 @@ +module ActiveSessionsHelper + # Maps a device type as defined in `ActiveSession` to an svg icon name and + # outputs the icon html. + # + # see `DeviceDetector::Device::DEVICE_NAMES` about the available device types + def active_session_device_type_icon(active_session) + icon_name = + case active_session.device_type + when 'smartphone', 'feature phone', 'phablet' + 'mobile' + when 'tablet' + 'tablet' + when 'tv', 'smart display', 'camera', 'portable media player', 'console' + 'media' + when 'car browser' + 'car' + else + 'monitor-o' + end + + sprite_icon(icon_name, size: 16, css_class: 'prepend-top-2') + end +end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 801e624e1de..eb81dc2de43 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -442,7 +442,7 @@ module ProjectsHelper visibilityHelpPath: help_page_path('public_access/public_access'), registryAvailable: Gitlab.config.registry.enabled, registryHelpPath: help_page_path('user/project/container_registry'), - lfsAvailable: Gitlab.config.lfs.enabled && current_user.admin?, + lfsAvailable: Gitlab.config.lfs.enabled, lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') } diff --git a/app/models/active_session.rb b/app/models/active_session.rb new file mode 100644 index 00000000000..b4a86dbb331 --- /dev/null +++ b/app/models/active_session.rb @@ -0,0 +1,110 @@ +class ActiveSession + include ActiveModel::Model + + attr_accessor :created_at, :updated_at, + :session_id, :ip_address, + :browser, :os, :device_name, :device_type + + def current?(session) + return false if session_id.nil? || session.id.nil? + + session_id == session.id + end + + def human_device_type + device_type&.titleize + end + + def self.set(user, request) + Gitlab::Redis::SharedState.with do |redis| + session_id = request.session.id + client = DeviceDetector.new(request.user_agent) + timestamp = Time.current + + active_user_session = new( + ip_address: request.ip, + browser: client.name, + os: client.os_name, + device_name: client.device_name, + device_type: client.device_type, + created_at: user.current_sign_in_at || timestamp, + updated_at: timestamp, + session_id: session_id + ) + + redis.pipelined do + redis.setex( + key_name(user.id, session_id), + Settings.gitlab['session_expire_delay'] * 60, + Marshal.dump(active_user_session) + ) + + redis.sadd( + lookup_key_name(user.id), + session_id + ) + end + end + end + + def self.list(user) + Gitlab::Redis::SharedState.with do |redis| + cleaned_up_lookup_entries(redis, user.id).map do |entry| + # rubocop:disable Security/MarshalLoad + Marshal.load(entry) + # rubocop:enable Security/MarshalLoad + end + end + end + + def self.destroy(user, session_id) + Gitlab::Redis::SharedState.with do |redis| + redis.srem(lookup_key_name(user.id), session_id) + + deleted_keys = redis.del(key_name(user.id, session_id)) + + # only allow deleting the devise session if we could actually find a + # related active session. this prevents another user from deleting + # someone else's session. + if deleted_keys > 0 + redis.del("#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}") + end + end + end + + def self.cleanup(user) + Gitlab::Redis::SharedState.with do |redis| + cleaned_up_lookup_entries(redis, user.id) + end + end + + def self.key_name(user_id, session_id = '*') + "#{Gitlab::Redis::SharedState::USER_SESSIONS_NAMESPACE}:#{user_id}:#{session_id}" + end + + def self.lookup_key_name(user_id) + "#{Gitlab::Redis::SharedState::USER_SESSIONS_LOOKUP_NAMESPACE}:#{user_id}" + end + + def self.cleaned_up_lookup_entries(redis, user_id) + lookup_key = lookup_key_name(user_id) + + session_ids = redis.smembers(lookup_key) + + entry_keys = session_ids.map { |session_id| key_name(user_id, session_id) } + return [] if entry_keys.empty? + + entries = redis.mget(entry_keys) + + session_ids_and_entries = session_ids.zip(entries) + + # remove expired keys. + # only the single key entries are automatically expired by redis, the + # lookup entries in the set need to be removed manually. + session_ids_and_entries.reject { |_session_id, entry| entry }.each do |session_id, _entry| + redis.srem(lookup_key, session_id) + end + + session_ids_and_entries.select { |_session_id, entry| entry }.map { |_session_id, entry| entry } + end +end diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 39676efa08c..3b952391b7e 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -13,7 +13,7 @@ module Ci after_save :update_project_statistics_after_save, if: :size_changed? after_destroy :update_project_statistics_after_destroy, unless: :project_destroyed? - after_save :update_file_store + after_save :update_file_store, if: :file_changed? scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) } diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 434b9b64c65..e1b9bc76475 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -530,6 +530,17 @@ module Ci @latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a end + # Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil. + # They always return `false`. + # These methods overwrite autogenerated ones to return correct results. + def unknown? + Gitlab.rails5? ? source.nil? : super + end + + def unknown_source? + Gitlab.rails5? ? config_source.nil? : super + end + private def ci_yaml_from_repo diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 75b8ea2a371..5a1eeb966aa 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -13,14 +13,27 @@ module Ci has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id has_many :builds, foreign_key: :stage_id - validates :project, presence: true, unless: :importing? - validates :pipeline, presence: true, unless: :importing? - validates :name, presence: true, unless: :importing? + with_options unless: :importing? do + validates :project, presence: true + validates :pipeline, presence: true + validates :name, presence: true + validates :position, presence: true + end - after_initialize do |stage| + after_initialize do self.status = DEFAULT_STATUS if self.status.nil? end + before_validation unless: :importing? do + next if position.present? + + self.position = statuses.select(:stage_idx) + .where('stage_idx IS NOT NULL') + .group(:stage_idx) + .order('COUNT(*) DESC') + .first&.stage_idx.to_i + end + state_machine :status, initial: :created do event :enqueue do transition created: :pending diff --git a/app/models/commit.rb b/app/models/commit.rb index 32f3d90595a..b46f9f34689 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -424,6 +424,12 @@ class Commit # no-op but needs to be defined since #persisted? is defined end + def touch_later + # No-op. + # This method is called by ActiveRecord. + # We don't want to do anything for `Commit` model, so this is empty. + end + WIP_REGEX = /\A\s*(((?i)(\[WIP\]|WIP:|WIP)\s|WIP$))|(fixup!|squash!)\s/.freeze def work_in_progress? diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index b6276c2fb50..97d89422594 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -189,4 +189,11 @@ class CommitStatus < ActiveRecord::Base v =~ /\d+/ ? v.to_i : v end end + + # Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil. + # They always return `false`. + # This method overwrites the autogenerated one to return correct result. + def unknown_failure? + Gitlab.rails5? ? failure_reason.nil? : super + end end diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 6b7f280fb70..84487031ee5 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -11,7 +11,7 @@ class LfsObject < ActiveRecord::Base mount_uploader :file, LfsObjectUploader - after_save :update_file_store + after_save :update_file_store, if: :file_changed? def update_file_store # The file.object_store is set during `uploader.store!` diff --git a/app/models/user.rb b/app/models/user.rb index b0668148972..4a602ffbb05 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -910,7 +910,7 @@ class User < ActiveRecord::Base def delete_async(deleted_by:, params: {}) block if params[:hard_delete] - DeleteUserWorker.perform_async(deleted_by.id, id, params) + DeleteUserWorker.perform_async(deleted_by.id, id, params.to_h) end def notification_service diff --git a/app/services/ci/ensure_stage_service.rb b/app/services/ci/ensure_stage_service.rb index 87f19b333de..b8c7be2d350 100644 --- a/app/services/ci/ensure_stage_service.rb +++ b/app/services/ci/ensure_stage_service.rb @@ -42,6 +42,7 @@ module Ci def create_stage Ci::Stage.create!(name: @build.stage, + position: @build.stage_idx, pipeline: @build.pipeline, project: @build.project) end diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index cedfcb50e09..2209a60a840 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -50,21 +50,30 @@ module MergeRequests end def commit - message = params[:commit_message] || merge_request.merge_commit_message - log_info("Git merge started on JID #{merge_jid}") - commit_id = repository.merge(current_user, source, merge_request, message) - log_info("Git merge finished on JID #{merge_jid} commit #{commit_id}") + commit_id = try_merge + + if commit_id + log_info("Git merge finished on JID #{merge_jid} commit #{commit_id}") + else + raise MergeError, 'Conflicts detected during merge' + end - raise MergeError, 'Conflicts detected during merge' unless commit_id + merge_request.update!(merge_commit_sha: commit_id) + end + + def try_merge + message = params[:commit_message] || merge_request.merge_commit_message - merge_request.update(merge_commit_sha: commit_id) + repository.merge(current_user, source, merge_request, message) rescue Gitlab::Git::HooksService::PreReceiveError => e - raise MergeError, e.message - rescue StandardError => e - raise MergeError, "Something went wrong during merge: #{e.message}" + handle_merge_error(log_message: e.message) + raise MergeError, 'Something went wrong during merge pre-receive hook' + rescue => e + handle_merge_error(log_message: e.message) + raise MergeError, 'Something went wrong during merge' ensure - merge_request.update(in_progress_merge_commit_sha: nil) + merge_request.update!(in_progress_merge_commit_sha: nil) end def after_merge diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index aeba9788fda..75ca5106fd5 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -13,7 +13,7 @@ .panel .panel-heading.alert.alert-danger Last repository check - = "(#{time_ago_in_words(@project.last_repository_check_at)} ago)" + = "(#{time_ago_with_tooltip(@project.last_repository_check_at)})" failed. See = link_to 'repocheck.log', admin_logs_path for error messages. diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml index e1cee584929..f90b8b8c0a4 100644 --- a/app/views/admin/runners/_runner.html.haml +++ b/app/views/admin/runners/_runner.html.haml @@ -31,7 +31,7 @@ = tag %td - if runner.contacted_at - #{time_ago_in_words(runner.contacted_at)} ago + = time_ago_with_tooltip runner.contacted_at - else Never %td.admin-runner-btn-group-cell diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 37269862de6..d04cf48b05c 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -108,4 +108,4 @@ %td.timestamp - if build.finished_at - %span #{time_ago_in_words build.finished_at} ago + %span= time_ago_with_tooltip build.finished_at diff --git a/app/views/admin/services/index.html.haml b/app/views/admin/services/index.html.haml index 50132572096..89872c1b91a 100644 --- a/app/views/admin/services/index.html.haml +++ b/app/views/admin/services/index.html.haml @@ -20,5 +20,4 @@ %td = service.description %td.light - = time_ago_in_words service.updated_at - ago + = time_ago_with_tooltip service.updated_at diff --git a/app/views/devise/mailer/unlock_instructions.html.haml b/app/views/devise/mailer/unlock_instructions.html.haml index 79e3a35cc9a..8ddfd3ea74a 100644 --- a/app/views/devise/mailer/unlock_instructions.html.haml +++ b/app/views/devise/mailer/unlock_instructions.html.haml @@ -2,7 +2,7 @@ = email_default_heading("Hello, #{@resource.name}!") %p Your GitLab account has been locked due to an excessive amount of unsuccessful - sign in attempts. Your account will automatically unlock in #{time_ago_in_words(Devise.unlock_in.from_now)} + sign in attempts. Your account will automatically unlock in #{distance_of_time_in_words(Devise.unlock_in)} or you may click the link below to unlock now. #cta = link_to('Unlock account', unlock_url(@resource, unlock_token: @token)) diff --git a/app/views/devise/mailer/unlock_instructions.text.erb b/app/views/devise/mailer/unlock_instructions.text.erb index 3aea3e20145..8d4abbf3500 100644 --- a/app/views/devise/mailer/unlock_instructions.text.erb +++ b/app/views/devise/mailer/unlock_instructions.text.erb @@ -1,7 +1,7 @@ Hello, <%= @resource.name %>! Your GitLab account has been locked due to an excessive amount of unsuccessful -sign in attempts. Your account will automatically unlock in <%= time_ago_in_words(Devise.unlock_in.from_now) %> +sign in attempts. Your account will automatically unlock in <%= distance_of_time_in_words(Devise.unlock_in) %> or you may click the link below to unlock now. <%= unlock_url(@resource, unlock_token: @token) %> diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml index 2ace1e2dd1e..65e95f3aeef 100644 --- a/app/views/groups/_group_admin_settings.html.haml +++ b/app/views/groups/_group_admin_settings.html.haml @@ -1,28 +1,26 @@ -- if current_user.admin? - .form-group - = f.label :lfs_enabled, 'Large File Storage', class: 'control-label' - .col-sm-10 - .checkbox - = f.label :lfs_enabled do - = f.check_box :lfs_enabled, checked: @group.lfs_enabled? - %strong - Allow projects within this group to use Git LFS - = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') - %br/ - %span.descr This setting can be overridden in each project. +.form-group + = f.label :lfs_enabled, 'Large File Storage', class: 'control-label' + .col-sm-10 + .checkbox + = f.label :lfs_enabled do + = f.check_box :lfs_enabled, checked: @group.lfs_enabled? + %strong + Allow projects within this group to use Git LFS + = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') + %br/ + %span.descr This setting can be overridden in each project. -- if can? current_user, :admin_group, @group - .form-group - = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2' - .col-sm-10 - .checkbox - = f.label :require_two_factor_authentication do - = f.check_box :require_two_factor_authentication - %strong - Require all users in this group to setup Two-factor authentication - = link_to icon('question-circle'), help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group') - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.text_field :two_factor_grace_period, class: 'form-control' - .help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication +.form-group + = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2' + .col-sm-10 + .checkbox + = f.label :require_two_factor_authentication do + = f.check_box :require_two_factor_authentication + %strong + Require all users in this group to setup Two-factor authentication + = link_to icon('question-circle'), help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group') +.form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.text_field :two_factor_grace_period, class: 'form-control' + .help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml index c878fcf2808..6cbd163dd41 100644 --- a/app/views/layouts/nav/sidebar/_profile.html.haml +++ b/app/views/layouts/nav/sidebar/_profile.html.haml @@ -129,6 +129,17 @@ = link_to profile_preferences_path do %strong.fly-out-top-item-name #{ _('Preferences') } + = nav_link(controller: :active_sessions) do + = link_to profile_active_sessions_path do + .nav-icon-container + = sprite_icon('monitor-lines') + %span.nav-item-name + Active Sessions + %ul.sidebar-sub-level-items.is-fly-out-only + = nav_link(controller: :active_sessions, html_options: { class: "fly-out-top-item" } ) do + = link_to profile_active_sessions_path do + %strong.fly-out-top-item-name + #{ _('Active Sessions') } = nav_link(path: 'profiles#audit_log') do = link_to audit_log_profile_path do .nav-icon-container diff --git a/app/views/peek/_bar.html.haml b/app/views/peek/_bar.html.haml index a911449672b..cb0cccb8f8a 100644 --- a/app/views/peek/_bar.html.haml +++ b/app/views/peek/_bar.html.haml @@ -3,5 +3,5 @@ #js-peek{ data: { env: Peek.env, request_id: Peek.request_id, peek_url: peek_routes.results_url, - profile_url: url_for(params.merge(lineprofiler: 'true')) }, + profile_url: url_for(safe_params.merge(lineprofiler: 'true')) }, class: Peek.env } diff --git a/app/views/profiles/active_sessions/_active_session.html.haml b/app/views/profiles/active_sessions/_active_session.html.haml new file mode 100644 index 00000000000..d40b771f48b --- /dev/null +++ b/app/views/profiles/active_sessions/_active_session.html.haml @@ -0,0 +1,31 @@ +- is_current_session = active_session.current?(session) + +%li + .pull-left.append-right-10{ data: { toggle: 'tooltip' }, title: active_session.human_device_type } + = active_session_device_type_icon(active_session) + + .description.pull-left + %div + %strong= active_session.ip_address + - if is_current_session + %div This is your current session + - else + %div + Last accessed on + = l(active_session.updated_at, format: :short) + + %div + %strong= active_session.browser + on + %strong= active_session.os + + %div + %strong Signed in + on + = l(active_session.created_at, format: :short) + + - unless is_current_session + .pull-right + = link_to profile_active_session_path(active_session.session_id), data: { confirm: 'Are you sure? The device will be signed out of GitLab.' }, method: :delete, class: "btn btn-danger prepend-left-10" do + %span.sr-only Revoke + Revoke diff --git a/app/views/profiles/active_sessions/index.html.haml b/app/views/profiles/active_sessions/index.html.haml new file mode 100644 index 00000000000..d0250bb4eab --- /dev/null +++ b/app/views/profiles/active_sessions/index.html.haml @@ -0,0 +1,14 @@ +- page_title 'Active Sessions' +- @content_class = "limit-container-width" unless fluid_layout + +.row.prepend-top-default + .col-lg-4.profile-settings-sidebar + %h4.prepend-top-0 + = page_title + %p + This is a list of devices that have logged into your account. Revoke any sessions that you do not recognize. + .col-lg-8 + .append-bottom-default + + %ul.well-list + = render partial: 'profiles/active_sessions/active_session', collection: @sessions diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 376f672f424..9f420ee86f7 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -8,7 +8,7 @@ .files-changed-inner .inline-parallel-buttons.hidden-xs.hidden-sm - if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? } - = link_to 'Expand all', url_for(params.merge(expanded: 1, format: nil)), class: 'btn btn-default' + = link_to 'Expand all', url_for(safe_params.merge(expanded: 1, format: nil)), class: 'btn btn-default' - if show_whitespace_toggle - if current_controller?(:commit) = commit_diff_whitespace_link(diffs.project, @commit, class: 'hidden-xs') diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml index 7f0bef5ede0..826404c2008 100644 --- a/app/views/projects/jobs/_sidebar.html.haml +++ b/app/views/projects/jobs/_sidebar.html.haml @@ -15,7 +15,7 @@ - elsif @build.has_expiring_artifacts? %p.build-detail-row The artifacts will be removed in - %span= time_ago_in_words @build.artifacts_expire_at + %span= time_ago_with_tooltip @build.artifacts_expire_at - if @build.artifacts? .btn-group.btn-group-justified{ role: :group } diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index f81db9b4e28..4e10511411f 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -3,7 +3,7 @@ = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f| .hide.alert.alert-danger.mr-compare-errors - .js-merge-request-new-compare.row{ 'data-target-project-url': project_new_merge_request_update_branches_path(@source_project), 'data-source-branch-url': project_new_merge_request_branch_from_path(@source_project), 'data-target-branch-url': project_new_merge_request_branch_to_path(@source_project) } + .js-merge-request-new-compare.row{ 'data-source-branch-url': project_new_merge_request_branch_from_path(@source_project), 'data-target-branch-url': project_new_merge_request_branch_to_path(@source_project) } .col-md-6 .panel.panel-default.panel-new-merge-request .panel-heading @@ -11,7 +11,7 @@ .panel-body.clearfix .merge-request-select.dropdown = f.hidden_field :source_project_id - = dropdown_toggle @merge_request.source_project_path, { toggle: "dropdown", field_name: "#{f.object_name}[source_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-source-project" } + = dropdown_toggle @merge_request.source_project_path, { toggle: "dropdown", 'field-name': "#{f.object_name}[source_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-source-project" } .dropdown-menu.dropdown-menu-selectable.dropdown-source-project = dropdown_title("Select source project") = dropdown_filter("Search projects") @@ -21,14 +21,12 @@ selected: f.object.source_project_id .merge-request-select.dropdown = f.hidden_field :source_branch - = dropdown_toggle f.object.source_branch || "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch git-revision-dropdown-toggle" } - .dropdown-menu.dropdown-menu-selectable.dropdown-source-branch.git-revision-dropdown - = dropdown_title("Select source branch") - = dropdown_filter("Search branches") - = dropdown_content do - = render 'projects/merge_requests/dropdowns/branch', - branches: @merge_request.source_branches, - selected: f.object.source_branch + = dropdown_toggle f.object.source_branch || _("Select source branch"), { toggle: "dropdown", 'field-name': "#{f.object_name}[source_branch]", 'refs-url': refs_project_path(@source_project), selected: f.object.source_branch }, { toggle_class: "js-compare-dropdown js-source-branch git-revision-dropdown-toggle" } + .dropdown-menu.dropdown-menu-selectable.js-source-branch-dropdown.git-revision-dropdown + = dropdown_title(_("Select source branch")) + = dropdown_filter(_("Search branches")) + = dropdown_content + = dropdown_loading .panel-footer .text-center= icon('spinner spin', class: 'js-source-loading') %ul.list-unstyled.mr_source_commit @@ -41,7 +39,7 @@ - projects = target_projects(@project) .merge-request-select.dropdown = f.hidden_field :target_project_id - = dropdown_toggle f.object.target_project.full_path, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" } + = dropdown_toggle f.object.target_project.full_path, { toggle: "dropdown", 'field-name': "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" } .dropdown-menu.dropdown-menu-selectable.dropdown-target-project = dropdown_title("Select target project") = dropdown_filter("Search projects") @@ -51,14 +49,12 @@ selected: f.object.target_project_id .merge-request-select.dropdown = f.hidden_field :target_branch - = dropdown_toggle f.object.target_branch, { toggle: "dropdown", field_name: "#{f.object_name}[target_branch]" }, { toggle_class: "js-compare-dropdown js-target-branch git-revision-dropdown-toggle" } - .dropdown-menu.dropdown-menu-selectable.dropdown-target-branch.js-target-branch-dropdown.git-revision-dropdown - = dropdown_title("Select target branch") - = dropdown_filter("Search branches") - = dropdown_content do - = render 'projects/merge_requests/dropdowns/branch', - branches: @merge_request.target_branches, - selected: f.object.target_branch + = dropdown_toggle f.object.target_branch, { toggle: "dropdown", 'field-name': "#{f.object_name}[target_branch]", 'refs-url': refs_project_path(f.object.target_project), selected: f.object.target_branch }, { toggle_class: "js-compare-dropdown js-target-branch git-revision-dropdown-toggle" } + .dropdown-menu.dropdown-menu-selectable.js-target-branch-dropdown.git-revision-dropdown + = dropdown_title(_("Select target branch")) + = dropdown_filter(_("Search branches")) + = dropdown_content + = dropdown_loading .panel-footer .text-center= icon('spinner spin', class: "js-target-loading") %ul.list-unstyled.mr_target_commit diff --git a/app/views/projects/merge_requests/dropdowns/_project.html.haml b/app/views/projects/merge_requests/dropdowns/_project.html.haml index aaf1ab00eeb..b3cf3c1d369 100644 --- a/app/views/projects/merge_requests/dropdowns/_project.html.haml +++ b/app/views/projects/merge_requests/dropdowns/_project.html.haml @@ -1,5 +1,5 @@ %ul - projects.each do |project| %li - %a{ href: "#", class: "#{('is-active' if selected == project.id)}", data: { id: project.id } } + %a{ href: "#", class: "#{('is-active' if selected == project.id)}", data: { id: project.id, 'refs-url': refs_project_path(project) } } = project.full_path diff --git a/app/views/projects/registry/repositories/_tag.html.haml b/app/views/projects/registry/repositories/_tag.html.haml index 0b082a2137f..0223372bff8 100644 --- a/app/views/projects/registry/repositories/_tag.html.haml +++ b/app/views/projects/registry/repositories/_tag.html.haml @@ -18,7 +18,7 @@ \- %td - if tag.created_at - = time_ago_in_words(tag.created_at) + = time_ago_with_tooltip tag.created_at - else .light \- diff --git a/app/views/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml index f33e7e25b68..322152cfaca 100644 --- a/app/views/projects/runners/show.html.haml +++ b/app/views/projects/runners/show.html.haml @@ -62,6 +62,6 @@ %td Last contact %td - if @runner.contacted_at - #{time_ago_in_words(@runner.contacted_at)} ago + = time_ago_with_tooltip @runner.contacted_at - else Never diff --git a/app/views/projects/services/_index.html.haml b/app/views/projects/services/_index.html.haml index 915c6b22162..dac7d4d1bbb 100644 --- a/app/views/projects/services/_index.html.haml +++ b/app/views/projects/services/_index.html.haml @@ -27,5 +27,4 @@ = service.description %td.light - if service.updated_at.present? - = time_ago_in_words service.updated_at - ago + = time_ago_with_tooltip service.updated_at diff --git a/app/views/projects/triggers/_trigger.html.haml b/app/views/projects/triggers/_trigger.html.haml index 6249c32b7cc..9201680f119 100644 --- a/app/views/projects/triggers/_trigger.html.haml +++ b/app/views/projects/triggers/_trigger.html.haml @@ -25,7 +25,7 @@ %td - if trigger.last_used - #{time_ago_in_words(trigger.last_used)} ago + = time_ago_with_tooltip trigger.last_used - else Never diff --git a/app/views/sherlock/transactions/_general.html.haml b/app/views/sherlock/transactions/_general.html.haml index 8533b130da6..a37fb5d449a 100644 --- a/app/views/sherlock/transactions/_general.html.haml +++ b/app/views/sherlock/transactions/_general.html.haml @@ -35,5 +35,4 @@ %span.light #{t('sherlock.finished_at')}: %strong - = time_ago_in_words(@transaction.finished_at) - = t('sherlock.ago') + = time_ago_with_tooltip @transaction.finished_at diff --git a/app/views/sherlock/transactions/index.html.haml b/app/views/sherlock/transactions/index.html.haml index bc05659dfa8..6ed7e9e21a6 100644 --- a/app/views/sherlock/transactions/index.html.haml +++ b/app/views/sherlock/transactions/index.html.haml @@ -35,8 +35,7 @@ = t('sherlock.seconds') %td= trans.queries.length %td - = time_ago_in_words(trans.finished_at) - = t('sherlock.ago') + = time_ago_with_tooltip trans.finished_at %td = link_to(sherlock_transaction_path(trans), class: 'btn btn-xs') do = t('sherlock.view') diff --git a/changelogs/unreleased/44775-avatar-on-os-fails-with-cdn.yml b/changelogs/unreleased/44775-avatar-on-os-fails-with-cdn.yml deleted file mode 100644 index 80b5b4a8abe..00000000000 --- a/changelogs/unreleased/44775-avatar-on-os-fails-with-cdn.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed wrong avatar URL when the avatar is on object storage. -merge_request: 18092 -author: -type: fixed diff --git a/changelogs/unreleased/45761-replace-actionview-time_ago_in_words.yml b/changelogs/unreleased/45761-replace-actionview-time_ago_in_words.yml new file mode 100644 index 00000000000..adf4db90407 --- /dev/null +++ b/changelogs/unreleased/45761-replace-actionview-time_ago_in_words.yml @@ -0,0 +1,5 @@ +--- +title: Replace time_ago_in_words with JS-based one +merge_request: 18607 +author: Takuya Noguchi +type: performance diff --git a/changelogs/unreleased/add-jwt-strategy-to-gitlab-suite.yml b/changelogs/unreleased/add-jwt-strategy-to-gitlab-suite.yml deleted file mode 100644 index 22a839cef56..00000000000 --- a/changelogs/unreleased/add-jwt-strategy-to-gitlab-suite.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Ports omniauth-jwt gem onto GitLab OmniAuth Strategies suite -merge_request: 18580 -author: -type: fixed diff --git a/changelogs/unreleased/blackst0ne-replace-spinach-project-source-markdown-render-feature.yml b/changelogs/unreleased/blackst0ne-replace-spinach-project-source-markdown-render-feature.yml new file mode 100644 index 00000000000..657ed782880 --- /dev/null +++ b/changelogs/unreleased/blackst0ne-replace-spinach-project-source-markdown-render-feature.yml @@ -0,0 +1,5 @@ +--- +title: Replace the `project/source/markdown_render.feature` spinach test with an rspec analog +merge_request: 18525 +author: "@blackst0ne" +type: other diff --git a/changelogs/unreleased/bvl-fix-maintainer-push-error.yml b/changelogs/unreleased/bvl-fix-maintainer-push-error.yml deleted file mode 100644 index 66ab8fbf884..00000000000 --- a/changelogs/unreleased/bvl-fix-maintainer-push-error.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix errors on pushing to an empty repository -merge_request: 18462 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-fix-openid-redirect.yml b/changelogs/unreleased/bvl-fix-openid-redirect.yml deleted file mode 100644 index 83ee6d953e4..00000000000 --- a/changelogs/unreleased/bvl-fix-openid-redirect.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix redirection error for applications using OpenID -merge_request: 18599 -author: -type: fixed diff --git a/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml b/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml deleted file mode 100644 index 9f057c67122..00000000000 --- a/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix commit trailer rendering when Gravatar is disabled -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/feature-display-active-sessions.yml b/changelogs/unreleased/feature-display-active-sessions.yml new file mode 100644 index 00000000000..14cfa66953e --- /dev/null +++ b/changelogs/unreleased/feature-display-active-sessions.yml @@ -0,0 +1,5 @@ +--- +title: Display active sessions and allow the user to revoke any of it +merge_request: 17867 +author: Alexis Reigel +type: added diff --git a/changelogs/unreleased/improve-quick-actions-summary-preview.yml b/changelogs/unreleased/improve-quick-actions-summary-preview.yml new file mode 100644 index 00000000000..bc75c169ad7 --- /dev/null +++ b/changelogs/unreleased/improve-quick-actions-summary-preview.yml @@ -0,0 +1,5 @@ +--- +title: Improve quick actions summary preview +merge_request: 18659 +author: George Tsiolis +type: changed diff --git a/changelogs/unreleased/increase-new-issue-metadata-form-margin.yml b/changelogs/unreleased/increase-new-issue-metadata-form-margin.yml new file mode 100644 index 00000000000..a7196f67969 --- /dev/null +++ b/changelogs/unreleased/increase-new-issue-metadata-form-margin.yml @@ -0,0 +1,5 @@ +--- +title: Increase new issue metadata form margin +merge_request: 18630 +author: George Tsiolis +type: fixed diff --git a/changelogs/unreleased/issue_45463.yml b/changelogs/unreleased/issue_45463.yml deleted file mode 100644 index a350568d04b..00000000000 --- a/changelogs/unreleased/issue_45463.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix users not seeing labels from private groups when being a member of a child project -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/jprovazn-generic-error.yml b/changelogs/unreleased/jprovazn-generic-error.yml new file mode 100644 index 00000000000..ced3b84fe02 --- /dev/null +++ b/changelogs/unreleased/jprovazn-generic-error.yml @@ -0,0 +1,6 @@ +--- +title: Display only generic message on merge error to avoid exposing any potentially + sensitive or user unfriendly backend messages. +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/jr-33320-lfs-settings-interface.yml b/changelogs/unreleased/jr-33320-lfs-settings-interface.yml new file mode 100644 index 00000000000..b39308f5474 --- /dev/null +++ b/changelogs/unreleased/jr-33320-lfs-settings-interface.yml @@ -0,0 +1,5 @@ +--- +title: Show group and project LFS settings in the interface to Owners and Masters +merge_request: 18562 +author: +type: changed diff --git a/changelogs/unreleased/refactor-move-mr-widget-wip-vue-component.yml b/changelogs/unreleased/refactor-move-mr-widget-wip-vue-component.yml new file mode 100644 index 00000000000..0f045431aae --- /dev/null +++ b/changelogs/unreleased/refactor-move-mr-widget-wip-vue-component.yml @@ -0,0 +1,5 @@ +--- +title: Move WorkInProgress vue component +merge_request: 17536 +author: George Tsiolis +type: performance diff --git a/changelogs/unreleased/refactor-move-no-tracking-pane-vue-component.yml b/changelogs/unreleased/refactor-move-no-tracking-pane-vue-component.yml new file mode 100644 index 00000000000..4bb088a1e58 --- /dev/null +++ b/changelogs/unreleased/refactor-move-no-tracking-pane-vue-component.yml @@ -0,0 +1,5 @@ +--- +title: Move TimeTrackingNoTrackingPane vue component +merge_request: 18676 +author: George Tsiolis +type: performance diff --git a/changelogs/unreleased/refactor-move-sidebar-time-tracking-vue-component.yml b/changelogs/unreleased/refactor-move-sidebar-time-tracking-vue-component.yml new file mode 100644 index 00000000000..4f578bfcf26 --- /dev/null +++ b/changelogs/unreleased/refactor-move-sidebar-time-tracking-vue-component.yml @@ -0,0 +1,5 @@ +--- +title: Move SidebarTimeTracking vue component +merge_request: 18677 +author: George Tsiolis +type: performance diff --git a/changelogs/unreleased/update-doorkeeper-changelog.yml b/changelogs/unreleased/update-doorkeeper-changelog.yml deleted file mode 100644 index b47bdf4a28d..00000000000 --- a/changelogs/unreleased/update-doorkeeper-changelog.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update doorkeeper to 4.3.2 to fix GitLab OAuth authentication -merge_request: 18543 -author: -type: fixed diff --git a/changelogs/unreleased/winh-new-mergerequest-branch-picker.yml b/changelogs/unreleased/winh-new-mergerequest-branch-picker.yml new file mode 100644 index 00000000000..401ecd09ef2 --- /dev/null +++ b/changelogs/unreleased/winh-new-mergerequest-branch-picker.yml @@ -0,0 +1,5 @@ +--- +title: Load branches on new merge request page asynchronously +merge_request: 18315 +author: +type: changed diff --git a/changelogs/unreleased/zj-namespace-service-mandatory.yml b/changelogs/unreleased/zj-namespace-service-mandatory.yml new file mode 100644 index 00000000000..d890741c51b --- /dev/null +++ b/changelogs/unreleased/zj-namespace-service-mandatory.yml @@ -0,0 +1,5 @@ +--- +title: Finish NamespaceService migration to Gitaly +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/zj-repo-checksum-opt-out.yml b/changelogs/unreleased/zj-repo-checksum-opt-out.yml new file mode 100644 index 00000000000..98dfedf7475 --- /dev/null +++ b/changelogs/unreleased/zj-repo-checksum-opt-out.yml @@ -0,0 +1,5 @@ +--- +title: Compute Gitlab::Git::Repository#checksum on Gitaly by default +merge_request: +author: +type: performance diff --git a/config/application.rb b/config/application.rb index ad7338763f7..09f706e3d70 100644 --- a/config/application.rb +++ b/config/application.rb @@ -115,6 +115,7 @@ module Gitlab config.assets.precompile << "test.css" config.assets.precompile << "snippets.css" config.assets.precompile << "locale/**/app.js" + config.assets.precompile << "emoji_sprites.css" # Import gitlab-svgs directly from vendored directory config.assets.paths << "#{config.root}/node_modules/@gitlab-org/gitlab-svgs/dist" diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index f2fde1e0048..da24881885e 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -15,19 +15,15 @@ cookie_key = if Rails.env.development? "_gitlab_session" end -if Rails.env.test? - Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" -else - sessions_config = Gitlab::Redis::SharedState.params - sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE +sessions_config = Gitlab::Redis::SharedState.params +sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE - Gitlab::Application.config.session_store( - :redis_store, # Using the cookie_store would enable session replay attacks. - servers: sessions_config, - key: cookie_key, - secure: Gitlab.config.gitlab.https, - httponly: true, - expires_in: Settings.gitlab['session_expire_delay'] * 60, - path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root - ) -end +Gitlab::Application.config.session_store( + :redis_store, # Using the cookie_store would enable session replay attacks. + servers: sessions_config, + key: cookie_key, + secure: Gitlab.config.gitlab.https, + httponly: true, + expires_in: Settings.gitlab['session_expire_delay'] * 60, + path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root +) diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb index ee034d21eae..bf079f8e1a7 100644 --- a/config/initializers/warden.rb +++ b/config/initializers/warden.rb @@ -6,4 +6,16 @@ Rails.application.configure do |config| Warden::Manager.before_failure do |env, opts| Gitlab::Auth::BlockedUserTracker.log_if_user_blocked(env) end + + Warden::Manager.after_authentication do |user, auth, opts| + ActiveSession.cleanup(user) + end + + Warden::Manager.after_set_user only: :fetch do |user, auth, opts| + ActiveSession.set(user, auth.request) + end + + Warden::Manager.before_logout do |user, auth, opts| + ActiveSession.destroy(user || auth.user, auth.request.session.id) + end end diff --git a/config/routes/profile.rb b/config/routes/profile.rb index bcfc17a5f66..a9ba5ac2c0b 100644 --- a/config/routes/profile.rb +++ b/config/routes/profile.rb @@ -30,6 +30,7 @@ resource :profile, only: [:show, :update] do put :revoke end end + resources :active_sessions, only: [:index, :destroy] resources :emails, only: [:index, :create, :destroy] do member do put :resend_confirmation_instructions diff --git a/config/routes/project.rb b/config/routes/project.rb index 2a1bcb8cde2..0d24c5a5d4f 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -161,7 +161,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end get :diff_for_path - get :update_branches get :branch_from get :branch_to end diff --git a/db/migrate/20180417101040_add_tmp_stage_priority_index_to_ci_builds.rb b/db/migrate/20180417101040_add_tmp_stage_priority_index_to_ci_builds.rb new file mode 100644 index 00000000000..ee82c70ecf8 --- /dev/null +++ b/db/migrate/20180417101040_add_tmp_stage_priority_index_to_ci_builds.rb @@ -0,0 +1,16 @@ +class AddTmpStagePriorityIndexToCiBuilds < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index(:ci_builds, [:stage_id, :stage_idx], + where: 'stage_idx IS NOT NULL', name: 'tmp_build_stage_position_index') + end + + def down + remove_concurrent_index_by_name(:ci_builds, 'tmp_build_stage_position_index') + end +end diff --git a/db/migrate/20180417101940_add_index_to_ci_stage.rb b/db/migrate/20180417101940_add_index_to_ci_stage.rb new file mode 100644 index 00000000000..9dac78db774 --- /dev/null +++ b/db/migrate/20180417101940_add_index_to_ci_stage.rb @@ -0,0 +1,9 @@ +class AddIndexToCiStage < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :ci_stages, :position, :integer + end +end diff --git a/db/post_migrate/20180420080616_schedule_stages_index_migration.rb b/db/post_migrate/20180420080616_schedule_stages_index_migration.rb new file mode 100644 index 00000000000..1d0daad002f --- /dev/null +++ b/db/post_migrate/20180420080616_schedule_stages_index_migration.rb @@ -0,0 +1,29 @@ +class ScheduleStagesIndexMigration < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + MIGRATION = 'MigrateStageIndex'.freeze + BATCH_SIZE = 10000 + + disable_ddl_transaction! + + class Stage < ActiveRecord::Base + include EachBatch + self.table_name = 'ci_stages' + end + + def up + disable_statement_timeout + + Stage.all.tap do |relation| + queue_background_migration_jobs_by_range_at_intervals(relation, + MIGRATION, + 5.minutes, + batch_size: BATCH_SIZE) + end + end + + def down + # noop + end +end diff --git a/db/schema.rb b/db/schema.rb index e2fe269079c..0b4b13c066c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -331,6 +331,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do add_index "ci_builds", ["project_id", "id"], name: "index_ci_builds_on_project_id_and_id", using: :btree add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree + add_index "ci_builds", ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)", using: :btree add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree @@ -495,6 +496,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do t.string "name" t.integer "status" t.integer "lock_version" + t.integer "position" end add_index "ci_stages", ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true, using: :btree diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md index b288ee95722..b469a9c6aef 100644 --- a/doc/development/fe_guide/icons.md +++ b/doc/development/fe_guide/icons.md @@ -49,7 +49,7 @@ Please use the following function inside JS to render an icon : All Icons and Illustrations are managed in the [gitlab-svgs](https://gitlab.com/gitlab-org/gitlab-svgs) repository which is added as a dev-dependency. -To upgrade to a new SVG Sprite version run `yarn upgrade @gitlab-org/gitlab-svgs` and then run `yarn run svg`. This task will copy the svg sprite and all illustrations in the correct folders. The updated files should be tracked in Git as those are referenced. +To upgrade to a new SVG Sprite version run `yarn upgrade @gitlab-org/gitlab-svgs`. # SVG Illustrations diff --git a/doc/user/profile/active_sessions.md b/doc/user/profile/active_sessions.md new file mode 100644 index 00000000000..5119c0e30d0 --- /dev/null +++ b/doc/user/profile/active_sessions.md @@ -0,0 +1,20 @@ +# Active Sessions + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17867) +> in GitLab 10.8. + +GitLab lists all devices that have logged into your account. This allows you to +review the sessions and revoke any of it that you don't recognize. + +## Listing all active sessions + +1. On the upper right corner, click on your avatar and go to your **Settings**. +1. Navigate to the **Active Sessions** tab. + + + +## Revoking a session + +1. Navigate to your [profile's](#profile-settings) **Settings > Active Sessions**. +1. Click on **Revoke** besides a session. The current session cannot be + revoked, as this would sign you out of GitLab. diff --git a/doc/user/profile/img/active_sessions_list.png b/doc/user/profile/img/active_sessions_list.png Binary files differnew file mode 100644 index 00000000000..76a52220bcd --- /dev/null +++ b/doc/user/profile/img/active_sessions_list.png diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index ab16f8d14c1..91cdef8d1dd 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -39,6 +39,7 @@ From there, you can: - Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH - Manage your [preferences](preferences.md#syntax-highlighting-theme) to customize your own GitLab experience +- [View your active sessions](active_sessions.md) and revoke any of them if necessary - Access your audit log, a security log of important events involving your account ## Changing your username diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index 0e29740b15f..0d592a6d43e 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -251,13 +251,4 @@ It is possible to host LFS objects externally by setting a custom LFS url with ` Because GitLab verifies the existence of objects referenced by LFS pointers, push will fail when LFS is enabled for the project. -LFS can be disabled for a project by Owners and Masters using the [Project API](../../api/projects.md#edit-project). - -```bash -curl --request PUT \ - --url https://example.com/api/v4/projects/<PROJECT_ID> \ - --header 'Private-Token: <YOUR_PRIVATE_TOKEN>' \ - --data 'lfs_enabled=false' -``` - -Note, `<PROJECT_ID>` can also be substituted with a [namespaced path](../../api/README.md#namespaced-path-encoding). +LFS can be disabled from the [Project settings](../../user/project/settings/index.md). diff --git a/features/project/source/markdown_render.feature b/features/project/source/markdown_render.feature deleted file mode 100644 index fe4466ad241..00000000000 --- a/features/project/source/markdown_render.feature +++ /dev/null @@ -1,147 +0,0 @@ -Feature: Project Source Markdown Render - Background: - Given I sign in as a user - And I own project "Delta" - And I visit markdown branch - - # Tree README - - @javascript - Scenario: Tree view should have correct links in README - Given I go directory which contains README file - And I click on a relative link in README - Then I should see the correct markdown - - @javascript - Scenario: I browse files from markdown branch - Then I should see files from repository in markdown - And I should see rendered README which contains correct links - And I click on Gitlab API in README - Then I should see correct document rendered - - @javascript - Scenario: I view README in markdown branch - Then I should see files from repository in markdown - And I should see rendered README which contains correct links - And I click on Rake tasks in README - Then I should see correct directory rendered - - @javascript - Scenario: I view README in markdown branch to see reference links to directory - Then I should see files from repository in markdown - And I should see rendered README which contains correct links - And I click on GitLab API doc directory in README - Then I should see correct doc/api directory rendered - - @javascript - Scenario: I view README in markdown branch to see reference links to file - Then I should see files from repository in markdown - And I should see rendered README which contains correct links - And I click on Maintenance in README - Then I should see correct maintenance file rendered - - @javascript - Scenario: README headers should have header links - Then I should see rendered README which contains correct links - And Header "Application details" should have correct id and link - - # Blob - - @javascript - Scenario: I navigate to doc directory to view documentation in markdown - And I navigate to the doc/api/README - And I see correct file rendered - And I click on users in doc/api/README - Then I should see the correct document file - - @javascript - Scenario: I navigate to doc directory to view user doc in markdown - And I navigate to the doc/api/README - And I see correct file rendered - And I click on raketasks in doc/api/README - Then I should see correct directory rendered - - @javascript - Scenario: I navigate to doc directory to view user doc in markdown - And I navigate to the doc/api/README - And Header "GitLab API" should have correct id and link - - # Markdown branch - - @javascript - Scenario: I browse files from markdown branch - When I visit markdown branch - Then I should see files from repository in markdown branch - And I should see rendered README which contains correct links - And I click on Gitlab API in README - Then I should see correct document rendered for markdown branch - - @javascript - Scenario: I browse directory from markdown branch - When I visit markdown branch - Then I should see files from repository in markdown branch - And I should see rendered README which contains correct links - And I click on Rake tasks in README - Then I should see correct directory rendered for markdown branch - - @javascript - Scenario: I navigate to doc directory to view documentation in markdown branch - When I visit markdown branch - And I navigate to the doc/api/README - And I see correct file rendered in markdown branch - And I click on users in doc/api/README - Then I should see the users document file in markdown branch - - @javascript - Scenario: I navigate to doc directory to view user doc in markdown branch - When I visit markdown branch - And I navigate to the doc/api/README - And I see correct file rendered in markdown branch - And I click on raketasks in doc/api/README - Then I should see correct directory rendered for markdown branch - - @javascript - Scenario: Tree markdown links view empty urls should have correct urls - When I visit markdown branch - Then The link with text "empty" should have url "tree/markdown" - When I visit markdown branch "README.md" blob - Then The link with text "empty" should have url "blob/markdown/README.md" - When I visit markdown branch "d" tree - Then The link with text "empty" should have url "tree/markdown/d" - When I visit markdown branch "d/README.md" blob - Then The link with text "empty" should have url "blob/markdown/d/README.md" - - # "ID" means "#id" on the tests below, because we are unable to escape the hash sign. - # which Spinach interprets as the start of a comment. - @javascript - Scenario: All markdown links with ids should have correct urls - When I visit markdown branch - Then The link with text "ID" should have url "tree/markdownID" - Then The link with text "/ID" should have url "tree/markdownID" - Then The link with text "README.mdID" should have url "blob/markdown/README.mdID" - Then The link with text "d/README.mdID" should have url "blob/markdown/d/README.mdID" - When I visit markdown branch "README.md" blob - Then The link with text "ID" should have url "blob/markdown/README.mdID" - Then The link with text "/ID" should have url "blob/markdown/README.mdID" - Then The link with text "README.mdID" should have url "blob/markdown/README.mdID" - Then The link with text "d/README.mdID" should have url "blob/markdown/d/README.mdID" - - # Wiki - - Scenario: I create a wiki page with different links - Given I go to wiki page - And I add various links to the wiki page - Then Wiki page should have added links - And I click on test link - Then I see new wiki page named test - When I go back to wiki page home - And I click on GitLab API doc link - Then I see Gitlab API document - When I go back to wiki page home - And I click on Rake tasks link - Then I see Rake tasks directory - - Scenario: Wiki headers should have should have ids generated for them. - Given I go to wiki page - And I add a header to the wiki page - Then Wiki header should have correct id and link diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index fd51ee1a316..82b931b2246 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -53,7 +53,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps first('.js-source-branch').click wait_for_requests - first('.dropdown-source-branch .dropdown-content a', text: 'fix').click + first('.js-source-branch-dropdown .dropdown-content a', text: 'fix').click click_button "Compare branches and continue" diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb deleted file mode 100644 index db99c179439..00000000000 --- a/features/steps/project/source/markdown_render.rb +++ /dev/null @@ -1,317 +0,0 @@ -# If you need to modify the existing seed repository for your tests, -# it is recommended that you make the changes on the `markdown` branch of the seed project repository, -# which should only be used by tests in this file. See `/spec/factories.rb#project` for more info. -class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedMarkdown - include WaitForRequests - - step 'I own project "Delta"' do - @project = ::Project.find_by(name: "Delta") - @project ||= create(:project, :repository, name: "Delta", namespace: @user.namespace) - @project.add_master(@user) - end - - step 'I should see files from repository in markdown' do - expect(current_path).to eq project_tree_path(@project, "markdown") - expect(page).to have_content "README.md" - expect(page).to have_content "CHANGELOG" - end - - step 'I should see rendered README which contains correct links' do - expect(page).to have_content "Welcome to GitLab GitLab is a free project and repository management application" - expect(page).to have_link "GitLab API doc" - expect(page).to have_link "GitLab API website" - expect(page).to have_link "Rake tasks" - expect(page).to have_link "backup and restore procedure" - expect(page).to have_link "GitLab API doc directory" - expect(page).to have_link "Maintenance" - end - - step 'I click on Gitlab API in README' do - click_link "GitLab API doc" - end - - step 'I should see correct document rendered' do - expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/README.md") - wait_for_requests - expect(page).to have_content "All API requests require authentication" - end - - step 'I click on Rake tasks in README' do - click_link "Rake tasks" - end - - step 'I should see correct directory rendered' do - expect(current_path).to eq project_tree_path(@project, "markdown/doc/raketasks") - expect(page).to have_content "backup_restore.md" - expect(page).to have_content "maintenance.md" - end - - step 'I click on GitLab API doc directory in README' do - click_link "GitLab API doc directory" - end - - step 'I should see correct doc/api directory rendered' do - expect(current_path).to eq project_tree_path(@project, "markdown/doc/api") - expect(page).to have_content "README.md" - expect(page).to have_content "users.md" - end - - step 'I click on Maintenance in README' do - click_link "Maintenance" - end - - step 'I should see correct maintenance file rendered' do - expect(current_path).to eq project_blob_path(@project, "markdown/doc/raketasks/maintenance.md") - wait_for_requests - expect(page).to have_content "bundle exec rake gitlab:env:info RAILS_ENV=production" - end - - step 'I click on link "empty" in the README' do - page.within('.readme-holder') do - click_link "empty" - end - end - - step 'I click on link "id" in the README' do - page.within('.readme-holder') do - click_link "#id" - end - end - - step 'I navigate to the doc/api/README' do - page.within '.tree-table' do - click_link "doc" - end - - page.within '.tree-table' do - click_link "api" - end - - wait_for_requests - - page.within '.tree-table' do - click_link "README.md" - end - end - - step 'I see correct file rendered' do - expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/README.md") - wait_for_requests - expect(page).to have_content "Contents" - expect(page).to have_link "Users" - expect(page).to have_link "Rake tasks" - end - - step 'I click on users in doc/api/README' do - click_link "Users" - end - - step 'I should see the correct document file' do - expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/users.md") - expect(page).to have_content "Get a list of users." - end - - step 'I click on raketasks in doc/api/README' do - click_link "Rake tasks" - end - - # Markdown branch - - When 'I visit markdown branch' do - visit project_tree_path(@project, "markdown") - wait_for_requests - end - - When 'I visit markdown branch "README.md" blob' do - visit project_blob_path(@project, "markdown/README.md") - end - - When 'I visit markdown branch "d" tree' do - visit project_tree_path(@project, "markdown/d") - end - - When 'I visit markdown branch "d/README.md" blob' do - visit project_blob_path(@project, "markdown/d/README.md") - end - - step 'I should see files from repository in markdown branch' do - expect(current_path).to eq project_tree_path(@project, "markdown") - expect(page).to have_content "README.md" - expect(page).to have_content "CHANGELOG" - end - - step 'I see correct file rendered in markdown branch' do - expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/README.md") - wait_for_requests - expect(page).to have_content "Contents" - expect(page).to have_link "Users" - expect(page).to have_link "Rake tasks" - end - - step 'I should see correct document rendered for markdown branch' do - expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/README.md") - wait_for_requests - expect(page).to have_content "All API requests require authentication" - end - - step 'I should see correct directory rendered for markdown branch' do - expect(current_path).to eq project_tree_path(@project, "markdown/doc/raketasks") - expect(page).to have_content "backup_restore.md" - expect(page).to have_content "maintenance.md" - end - - step 'I should see the users document file in markdown branch' do - expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/users.md") - expect(page).to have_content "Get a list of users." - end - - # Expected link contents - - step 'The link with text "empty" should have url "tree/markdown"' do - wait_for_requests - find('a', text: /^empty$/)['href'] == current_host + project_tree_path(@project, "markdown") - end - - step 'The link with text "empty" should have url "blob/markdown/README.md"' do - find('a', text: /^empty$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") - end - - step 'The link with text "empty" should have url "tree/markdown/d"' do - find('a', text: /^empty$/)['href'] == current_host + project_tree_path(@project, "markdown/d") - end - - step 'The link with text "empty" should have '\ - 'url "blob/markdown/d/README.md"' do - find('a', text: /^empty$/)['href'] == current_host + project_blob_path(@project, "markdown/d/README.md") - end - - step 'The link with text "ID" should have url "tree/markdownID"' do - find('a', text: /^#id$/)['href'] == current_host + project_tree_path(@project, "markdown") + '#id' - end - - step 'The link with text "/ID" should have url "tree/markdownID"' do - find('a', text: %r{^/#id$})['href'] == current_host + project_tree_path(@project, "markdown") + '#id' - end - - step 'The link with text "README.mdID" '\ - 'should have url "blob/markdown/README.mdID"' do - find('a', text: /^README.md#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id' - end - - step 'The link with text "d/README.mdID" should have '\ - 'url "blob/markdown/d/README.mdID"' do - find('a', text: %r{^d/README.md#id$})['href'] == current_host + project_blob_path(@project, "d/markdown/README.md") + '#id' - end - - step 'The link with text "ID" should have url "blob/markdown/README.mdID"' do - wait_for_requests - find('a', text: /^#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id' - end - - step 'The link with text "/ID" should have url "blob/markdown/README.mdID"' do - find('a', text: %r{^/#id$})['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id' - end - - # Wiki - - step 'I go to wiki page' do - first(:link, "Wiki").click - expect(current_path).to eq project_wiki_path(@project, "home") - end - - step 'I add various links to the wiki page' do - fill_in "wiki[content]", with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n" - fill_in "wiki[message]", with: "Adding links to wiki" - page.within '.wiki-form' do - click_button "Create page" - end - end - - step 'Wiki page should have added links' do - expect(current_path).to eq project_wiki_path(@project, "home") - expect(page).to have_content "test GitLab API doc Rake tasks" - end - - step 'I add a header to the wiki page' do - fill_in "wiki[content]", with: "# Wiki header\n" - fill_in "wiki[message]", with: "Add header to wiki" - page.within '.wiki-form' do - click_button "Create page" - end - end - - step 'Wiki header should have correct id and link' do - header_should_have_correct_id_and_link(1, 'Wiki header', 'wiki-header') - end - - step 'I click on test link' do - click_link "test" - end - - step 'I see new wiki page named test' do - expect(current_path).to eq project_wiki_path(@project, "test") - - page.within(:css, ".nav-text") do - expect(page).to have_content "Test" - expect(page).to have_content "Create Page" - end - end - - When 'I go back to wiki page home' do - visit project_wiki_path(@project, "home") - expect(current_path).to eq project_wiki_path(@project, "home") - end - - step 'I click on GitLab API doc link' do - click_link "GitLab API" - end - - step 'I see Gitlab API document' do - expect(current_path).to eq project_wiki_path(@project, "api") - - page.within(:css, ".nav-text") do - expect(page).to have_content "Create" - expect(page).to have_content "Api" - end - end - - step 'I click on Rake tasks link' do - click_link "Rake tasks" - end - - step 'I see Rake tasks directory' do - expect(current_path).to eq project_wiki_path(@project, "raketasks") - - page.within(:css, ".nav-text") do - expect(page).to have_content "Create" - expect(page).to have_content "Rake" - end - end - - step 'I go directory which contains README file' do - visit project_tree_path(@project, "markdown/doc/api") - expect(current_path).to eq project_tree_path(@project, "markdown/doc/api") - end - - step 'I click on a relative link in README' do - click_link "Users" - end - - step 'I should see the correct markdown' do - expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/users.md") - wait_for_requests - expect(page).to have_content "List users" - end - - step 'Header "Application details" should have correct id and link' do - wait_for_requests - header_should_have_correct_id_and_link(2, 'Application details', 'application-details') - end - - step 'Header "GitLab API" should have correct id and link' do - header_should_have_correct_id_and_link(1, 'GitLab API', 'gitlab-api') - end -end diff --git a/features/steps/shared/markdown.rb b/features/steps/shared/markdown.rb index 9d522936fb6..65118f07ca2 100644 --- a/features/steps/shared/markdown.rb +++ b/features/steps/shared/markdown.rb @@ -1,15 +1,6 @@ module SharedMarkdown include Spinach::DSL - def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki") - node = find("#{parent} h#{level} a#user-content-#{id}") - expect(node[:href]).to end_with "##{id}" - - # Work around a weird Capybara behavior where calling `parent` on a node - # returns the whole document, not the node's actual parent element - expect(find(:xpath, "#{node.path}/..").text).to eq text - end - step 'I should not see the Markdown preview' do expect(find('.gfm-form .js-md-preview')).not_to be_visible end diff --git a/lib/gitlab/background_migration/migrate_stage_index.rb b/lib/gitlab/background_migration/migrate_stage_index.rb new file mode 100644 index 00000000000..f90f35a913d --- /dev/null +++ b/lib/gitlab/background_migration/migrate_stage_index.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true +# rubocop:disable Style/Documentation + +module Gitlab + module BackgroundMigration + class MigrateStageIndex + def perform(start_id, stop_id) + migrate_stage_index_sql(start_id.to_i, stop_id.to_i).tap do |sql| + ActiveRecord::Base.connection.execute(sql) + end + end + + private + + def migrate_stage_index_sql(start_id, stop_id) + if Gitlab::Database.postgresql? + <<~SQL + WITH freqs AS ( + SELECT stage_id, stage_idx, COUNT(*) AS freq FROM ci_builds + WHERE stage_id BETWEEN #{start_id} AND #{stop_id} + AND stage_idx IS NOT NULL + GROUP BY stage_id, stage_idx + ), indexes AS ( + SELECT DISTINCT stage_id, last_value(stage_idx) + OVER (PARTITION BY stage_id ORDER BY freq ASC) AS index + FROM freqs + ) + + UPDATE ci_stages SET position = indexes.index + FROM indexes WHERE indexes.stage_id = ci_stages.id + AND ci_stages.position IS NULL; + SQL + else + <<~SQL + UPDATE ci_stages + SET position = + (SELECT stage_idx FROM ci_builds + WHERE ci_builds.stage_id = ci_stages.id + GROUP BY ci_builds.stage_idx ORDER BY COUNT(*) DESC LIMIT 1) + WHERE ci_stages.id BETWEEN #{start_id} AND #{stop_id} + AND ci_stages.position IS NULL + SQL + end + end + end + end +end diff --git a/lib/gitlab/ci/cron_parser.rb b/lib/gitlab/ci/cron_parser.rb index 551483d0aaa..73f36735e35 100644 --- a/lib/gitlab/ci/cron_parser.rb +++ b/lib/gitlab/ci/cron_parser.rb @@ -6,7 +6,7 @@ module Gitlab def initialize(cron, cron_timezone = 'UTC') @cron = cron - @cron_timezone = ActiveSupport::TimeZone.find_tzinfo(cron_timezone).name + @cron_timezone = timezone_name(cron_timezone) end def next_time_from(time) @@ -24,6 +24,12 @@ module Gitlab private + def timezone_name(timezone) + ActiveSupport::TimeZone.find_tzinfo(timezone).name + rescue TZInfo::InvalidTimezoneIdentifier + timezone + end + # NOTE: # cron_timezone can only accept timezones listed in TZInfo::Timezone. # Aliases of Timezones from ActiveSupport::TimeZone are NOT accepted, diff --git a/lib/gitlab/ci/pipeline/seed/stage.rb b/lib/gitlab/ci/pipeline/seed/stage.rb index c101f30d6e8..2b58d9863a0 100644 --- a/lib/gitlab/ci/pipeline/seed/stage.rb +++ b/lib/gitlab/ci/pipeline/seed/stage.rb @@ -19,6 +19,7 @@ module Gitlab def attributes { name: @attributes.fetch(:name), + position: @attributes.fetch(:index), pipeline: @pipeline, project: @pipeline.project } end diff --git a/lib/gitlab/database/arel_methods.rb b/lib/gitlab/database/arel_methods.rb new file mode 100644 index 00000000000..d7e3ce08b32 --- /dev/null +++ b/lib/gitlab/database/arel_methods.rb @@ -0,0 +1,18 @@ +module Gitlab + module Database + module ArelMethods + private + + # In Arel 7.0.0 (Arel 7.1.4 is used in Rails 5.0) the `engine` parameter of `Arel::UpdateManager#initializer` + # was removed. + # Remove this file and inline this method when removing rails5? code. + def arel_update_manager + if Gitlab.rails5? + Arel::UpdateManager.new + else + Arel::UpdateManager.new(ActiveRecord::Base) + end + end + end + end +end diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb index 77079e5e72b..c21bae5e16b 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -1,6 +1,8 @@ module Gitlab module Database module MigrationHelpers + include Gitlab::Database::ArelMethods + BACKGROUND_MIGRATION_BATCH_SIZE = 1000 # Number of rows to process per job BACKGROUND_MIGRATION_JOB_BUFFER_SIZE = 1000 # Number of jobs to bulk queue at a time @@ -314,7 +316,7 @@ module Gitlab stop_arel = yield table, stop_arel if block_given? stop_row = exec_query(stop_arel.to_sql).to_hash.first - update_arel = Arel::UpdateManager.new(ActiveRecord::Base) + update_arel = arel_update_manager .table(table) .set([[table[column], value]]) .where(table[:id].gteq(start_id)) diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb index 1a697396ff1..14de28a1d08 100644 --- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb +++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb @@ -3,6 +3,8 @@ module Gitlab module RenameReservedPathsMigration module V1 class RenameBase + include Gitlab::Database::ArelMethods + attr_reader :paths, :migration delegate :update_column_in_batches, @@ -62,10 +64,10 @@ module Gitlab old_full_path, new_full_path) - update = Arel::UpdateManager.new(ActiveRecord::Base) - .table(routes) - .set([[routes[:path], replace_statement]]) - .where(Arel::Nodes::SqlLiteral.new(filter)) + update = arel_update_manager + .table(routes) + .set([[routes[:path], replace_statement]]) + .where(Arel::Nodes::SqlLiteral.new(filter)) execute(update.to_sql) end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index de0044fc149..84d37f77fbb 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1569,7 +1569,8 @@ module Gitlab end def checksum - gitaly_migrate(:calculate_checksum) do |is_enabled| + gitaly_migrate(:calculate_checksum, + status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| if is_enabled gitaly_repository_client.calculate_checksum else diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index a7e055ac444..c741dabe168 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -19,6 +19,7 @@ module Gitlab gon.gitlab_logo = ActionController::Base.helpers.asset_path('gitlab_logo.png') gon.sprite_icons = IconsHelper.sprite_icon_path gon.sprite_file_icons = IconsHelper.sprite_file_icons_path + gon.emoji_sprites_css_path = ActionController::Base.helpers.stylesheet_path('emoji_sprites') gon.test_env = Rails.env.test? gon.suggested_label_colors = LabelsHelper.suggested_colors diff --git a/lib/gitlab/redis/shared_state.rb b/lib/gitlab/redis/shared_state.rb index 10bec7a90da..e5a0fdae7ef 100644 --- a/lib/gitlab/redis/shared_state.rb +++ b/lib/gitlab/redis/shared_state.rb @@ -5,6 +5,8 @@ module Gitlab module Redis class SharedState < ::Gitlab::Redis::Wrapper SESSION_NAMESPACE = 'session:gitlab'.freeze + USER_SESSIONS_NAMESPACE = 'session:user:gitlab'.freeze + USER_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:user:gitlab'.freeze DEFAULT_REDIS_SHARED_STATE_URL = 'redis://localhost:6382'.freeze REDIS_SHARED_STATE_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_SHARED_STATE_CONFIG_FILE'.freeze diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 156115f8a8f..4a691d640b3 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -294,17 +294,7 @@ module Gitlab # add_namespace("default", "gitlab") # def add_namespace(storage, name) - Gitlab::GitalyClient.migrate(:add_namespace, - status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled| - if enabled - Gitlab::GitalyClient::NamespaceService.new(storage).add(name) - else - path = full_path(storage, name) - FileUtils.mkdir_p(path, mode: 0770) unless exists?(storage, name) - end - end - rescue Errno::EEXIST => e - Rails.logger.warn("Directory exists as a file: #{e} at: #{path}") + Gitlab::GitalyClient::NamespaceService.new(storage).add(name) rescue GRPC::InvalidArgument => e raise ArgumentError, e.message end @@ -316,14 +306,7 @@ module Gitlab # rm_namespace("default", "gitlab") # def rm_namespace(storage, name) - Gitlab::GitalyClient.migrate(:remove_namespace, - status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled| - if enabled - Gitlab::GitalyClient::NamespaceService.new(storage).remove(name) - else - FileUtils.rm_r(full_path(storage, name), force: true) - end - end + Gitlab::GitalyClient::NamespaceService.new(storage).remove(name) rescue GRPC::InvalidArgument => e raise ArgumentError, e.message end @@ -335,17 +318,7 @@ module Gitlab # mv_namespace("/path/to/storage", "gitlab", "gitlabhq") # def mv_namespace(storage, old_name, new_name) - Gitlab::GitalyClient.migrate(:rename_namespace, - status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled| - if enabled - Gitlab::GitalyClient::NamespaceService.new(storage) - .rename(old_name, new_name) - else - break false if exists?(storage, new_name) || !exists?(storage, old_name) - - FileUtils.mv(full_path(storage, old_name), full_path(storage, new_name)) - end - end + Gitlab::GitalyClient::NamespaceService.new(storage).rename(old_name, new_name) rescue GRPC::InvalidArgument false end @@ -370,17 +343,8 @@ module Gitlab # exists?(storage, 'gitlab') # exists?(storage, 'gitlab/cookies.git') # - # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/385 def exists?(storage, dir_name) - Gitlab::GitalyClient.migrate(:namespace_exists, - status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled| - if enabled - Gitlab::GitalyClient::NamespaceService.new(storage) - .exists?(dir_name) - else - File.exist?(full_path(storage, dir_name)) - end - end + Gitlab::GitalyClient::NamespaceService.new(storage).exists?(dir_name) end protected diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb index e14ba29fa70..715bb9f5e52 100644 --- a/spec/controllers/projects/clusters/gcp_controller_spec.rb +++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb @@ -142,7 +142,7 @@ describe Projects::Clusters::GcpController do context 'when google project billing is enabled' do before do - redis_double = double + redis_double = double.as_null_object allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double) allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true') end diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb index 24310b847e8..00d76f3c39a 100644 --- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb @@ -157,34 +157,4 @@ describe Projects::MergeRequests::CreationsController do expect(response).to have_gitlab_http_status(200) end end - - describe 'GET #update_branches' do - before do - allow(Ability).to receive(:allowed?).and_call_original - end - - it 'lists the branches of another fork if the user has access' do - expect(Ability).to receive(:allowed?).with(user, :read_project, project) { true } - - get :update_branches, - namespace_id: fork_project.namespace, - project_id: fork_project, - target_project_id: project.id - - expect(assigns(:target_branches)).not_to be_empty - expect(response).to have_gitlab_http_status(200) - end - - it 'does not list branches when the user cannot read the project' do - expect(Ability).to receive(:allowed?).with(user, :read_project, project) { false } - - get :update_branches, - namespace_id: fork_project.namespace, - project_id: fork_project, - target_project_id: project.id - - expect(response).to have_gitlab_http_status(200) - expect(assigns(:target_branches)).to eq([]) - end - end end diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 08e2ccf893a..c3468536ae1 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -54,9 +54,9 @@ describe Projects::RawController do end context 'and lfs uses object storage' do + let(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') } + before do - lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") - lfs_object.save! stub_lfs_object_storage lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE) end diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb index 25309033571..ce61e6bf759 100644 --- a/spec/factories/ci/stages.rb +++ b/spec/factories/ci/stages.rb @@ -21,6 +21,7 @@ FactoryBot.define do pipeline factory: :ci_empty_pipeline name 'test' + position 1 status 'pending' end end diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb index ce5fbc343ee..53368c64e10 100644 --- a/spec/factories/commit_statuses.rb +++ b/spec/factories/commit_statuses.rb @@ -2,6 +2,7 @@ FactoryBot.define do factory :commit_status, class: CommitStatus do name 'default' stage 'test' + stage_idx 0 status 'success' description 'commit status' pipeline factory: :ci_pipeline_with_one_job diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index f1ac73ff819..90cf5a53787 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -19,7 +19,7 @@ feature 'Admin uses repository checks' do expect(page).to have_content('Repository check was triggered') end - scenario 'to see a single failed repository check' do + scenario 'to see a single failed repository check', :js do project = create(:project) project.update_columns( last_repository_check_failed: true, diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb index ff2a0e15719..ddd64fa1412 100644 --- a/spec/features/issues/user_uses_slash_commands_spec.rb +++ b/spec/features/issues/user_uses_slash_commands_spec.rb @@ -178,9 +178,10 @@ feature 'Issues > User uses quick actions', :js do end context 'when the project is valid but the user not authorized' do - let(:project_unauthorized) {create(:project, :public)} + let(:project_unauthorized) { create(:project, :public) } before do + gitlab_sign_out sign_in(user) visit project_issue_path(project, issue) end @@ -195,6 +196,7 @@ feature 'Issues > User uses quick actions', :js do context 'when the project is invalid' do before do + gitlab_sign_out sign_in(user) visit project_issue_path(project, issue) end diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb index dbca279569a..42c279af117 100644 --- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb +++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb @@ -19,7 +19,7 @@ describe 'Merge request > User selects branches for new MR', :js do expect(page).to have_content('Target branch') first('.js-source-branch').click - find('.dropdown-source-branch .dropdown-content a', match: :first).click + find('.js-source-branch-dropdown .dropdown-content a', match: :first).click expect(page).to have_content "b83d6e3" end @@ -35,22 +35,16 @@ describe 'Merge request > User selects branches for new MR', :js do expect(page).to have_content('Target branch') first('.js-target-branch').click - find('.dropdown-target-branch .dropdown-content a', text: 'v1.1.0', match: :first).click + find('.js-target-branch-dropdown .dropdown-content a', text: 'v1.1.0', match: :first).click expect(page).to have_content "b83d6e3" end it 'generates a diff for an orphaned branch' do - visit project_merge_requests_path(project) - - page.within '.content' do - click_link 'New merge request' - end - expect(page).to have_content('Source branch') - expect(page).to have_content('Target branch') + visit project_new_merge_request_path(project) find('.js-source-branch', match: :first).click - find('.dropdown-source-branch .dropdown-content a', text: 'orphaned-branch', match: :first).click + find('.js-source-branch-dropdown .dropdown-content a', text: 'orphaned-branch', match: :first).click click_button "Compare branches" click_link "Changes" @@ -71,19 +65,18 @@ describe 'Merge request > User selects branches for new MR', :js do first('.js-source-branch').click - input = find('.dropdown-source-branch .dropdown-input-field') - input.click - input.send_keys('orphaned-branch') + page.within '.js-source-branch-dropdown' do + input = find('.dropdown-input-field') + input.click + input.send_keys('orphaned-branch') - find('.dropdown-source-branch .dropdown-content li', match: :first) - source_items = all('.dropdown-source-branch .dropdown-content li') - - expect(source_items.count).to eq(1) + expect(page).to have_css('.dropdown-content li', count: 1) + end first('.js-target-branch').click - find('.dropdown-target-branch .dropdown-content li', match: :first) - target_items = all('.dropdown-target-branch .dropdown-content li') + find('.js-target-branch-dropdown .dropdown-content li', match: :first) + target_items = all('.js-target-branch-dropdown .dropdown-content li') expect(target_items.count).to be > 1 end @@ -171,7 +164,6 @@ describe 'Merge request > User selects branches for new MR', :js do page.within('.merge-request') do click_link 'Pipelines' - wait_for_requests expect(page).to have_content "##{pipeline.id}" end diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb new file mode 100644 index 00000000000..4045cfd21c4 --- /dev/null +++ b/spec/features/profiles/active_sessions_spec.rb @@ -0,0 +1,89 @@ +require 'rails_helper' + +feature 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do + let(:user) do + create(:user).tap do |user| + user.current_sign_in_at = Time.current + end + end + + around do |example| + Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do + example.run + end + end + + scenario 'User sees their active sessions' do + Capybara::Session.new(:session1) + Capybara::Session.new(:session2) + + # note: headers can only be set on the non-js (aka. rack-test) driver + using_session :session1 do + Capybara.page.driver.header( + 'User-Agent', + 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0' + ) + + gitlab_sign_in(user) + end + + # set an additional session on another device + using_session :session2 do + Capybara.page.driver.header( + 'User-Agent', + 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]' + ) + + gitlab_sign_in(user) + end + + using_session :session1 do + visit profile_active_sessions_path + + expect(page).to have_content( + '127.0.0.1 ' \ + 'This is your current session ' \ + 'Firefox on Ubuntu ' \ + 'Signed in on 12 Mar 09:06' + ) + + expect(page).to have_selector '[title="Desktop"]', count: 1 + + expect(page).to have_content( + '127.0.0.1 ' \ + 'Last accessed on 12 Mar 09:06 ' \ + 'Mobile Safari on iOS ' \ + 'Signed in on 12 Mar 09:06' + ) + + expect(page).to have_selector '[title="Smartphone"]', count: 1 + end + end + + scenario 'User can revoke a session', :js, :redis_session_store do + Capybara::Session.new(:session1) + Capybara::Session.new(:session2) + + # set an additional session in another browser + using_session :session2 do + gitlab_sign_in(user) + end + + using_session :session1 do + gitlab_sign_in(user) + visit profile_active_sessions_path + + expect(page).to have_link('Revoke', count: 1) + + accept_confirm { click_on 'Revoke' } + + expect(page).not_to have_link('Revoke') + end + + using_session :session2 do + visit profile_active_sessions_path + + expect(page).to have_content('You need to sign in or sign up before continuing.') + end + end +end diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb index 9c1f11f4c12..41f6c52fb8a 100644 --- a/spec/features/projects/files/user_browses_files_spec.rb +++ b/spec/features/projects/files/user_browses_files_spec.rb @@ -1,14 +1,12 @@ -require 'spec_helper' +require "spec_helper" -describe 'Projects > Files > User browses files' do +describe "User browses files" do let(:fork_message) do "You're not allowed to make changes to this project directly. "\ "A fork of this project has been created that you can make changes in, so you can submit a merge request." end - let(:project) { create(:project, :repository, name: 'Shop') } - let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } - let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } - let(:tree_path_ref_6d39438) { project_tree_path(project, '6d39438') } + let(:project) { create(:project, :repository, name: "Shop") } + let(:project2) { create(:project, :repository, name: "Another Project", path: "another-project") } let(:tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } let(:user) { project.owner } @@ -16,57 +14,55 @@ describe 'Projects > Files > User browses files' do sign_in(user) end - it 'shows last commit for current directory' do + it "shows last commit for current directory" do visit(tree_path_root_ref) - click_link 'files' + click_link("files") - last_commit = project.repository.last_commit_for_path(project.default_branch, 'files') - page.within('.blob-commit-info') do - expect(page).to have_content last_commit.short_id - expect(page).to have_content last_commit.author_name + last_commit = project.repository.last_commit_for_path(project.default_branch, "files") + + page.within(".blob-commit-info") do + expect(page).to have_content(last_commit.short_id).and have_content(last_commit.author_name) end end - context 'when browsing the master branch' do + context "when browsing the master branch" do before do visit(tree_path_root_ref) end - it 'shows files from a repository' do - expect(page).to have_content('VERSION') - expect(page).to have_content('.gitignore') - expect(page).to have_content('LICENSE') + it "shows files from a repository" do + expect(page).to have_content("VERSION") + .and have_content(".gitignore") + .and have_content("LICENSE") end - it 'shows the "Browse Directory" link' do - click_link('files') - click_link('History') + it "shows the `Browse Directory` link" do + click_link("files") + click_link("History") - expect(page).to have_link('Browse Directory') - expect(page).not_to have_link('Browse Code') + expect(page).to have_link("Browse Directory").and have_no_link("Browse Code") end - it 'shows the "Browse File" link' do - page.within('.tree-table') do - click_link('README.md') + it "shows the `Browse File` link" do + page.within(".tree-table") do + click_link("README.md") end - click_link('History') - expect(page).to have_link('Browse File') - expect(page).not_to have_link('Browse Files') + click_link("History") + + expect(page).to have_link("Browse File").and have_no_link("Browse Files") end - it 'shows the "Browse Files" link' do - click_link('History') + it "shows the `Browse Files` link" do + click_link("History") - expect(page).to have_link('Browse Files') - expect(page).not_to have_link('Browse Directory') + expect(page).to have_link("Browse Files").and have_no_link("Browse Directory") end - it 'redirects to the permalink URL' do - click_link('.gitignore') - click_link('Permalink') + it "redirects to the permalink URL" do + click_link(".gitignore") + click_link("Permalink") permalink_path = project_blob_path(project, "#{project.repository.commit.sha}/.gitignore") @@ -74,80 +70,180 @@ describe 'Projects > Files > User browses files' do end end - context 'when browsing a specific ref' do + context "when browsing the `markdown` branch", :js do + context "when browsing the root" do + before do + visit(project_tree_path(project, "markdown")) + end + + it "shows correct files and links" do + # rubocop:disable Lint/Void + # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`. + find("a", text: /^empty$/)["href"] == project_tree_url(project, "markdown") + find("a", text: /^#id$/)["href"] == project_tree_url(project, "markdown", anchor: "#id") + find("a", text: %r{^/#id$})["href"] == project_tree_url(project, "markdown", anchor: "#id") + find("a", text: /^README.md#id$/)["href"] == project_blob_url(project, "markdown/README.md", anchor: "#id") + find("a", text: %r{^d/README.md#id$})["href"] == project_blob_url(project, "d/markdown/README.md", anchor: "#id") + # rubocop:enable Lint/Void + + expect(current_path).to eq(project_tree_path(project, "markdown")) + expect(page).to have_content("README.md") + .and have_content("CHANGELOG") + .and have_content("Welcome to GitLab GitLab is a free project and repository management application") + .and have_link("GitLab API doc") + .and have_link("GitLab API website") + .and have_link("Rake tasks") + .and have_link("backup and restore procedure") + .and have_link("GitLab API doc directory") + .and have_link("Maintenance") + .and have_header_with_correct_id_and_link(2, "Application details", "application-details") + end + + it "shows correct content of file" do + click_link("GitLab API doc") + + expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/README.md")) + expect(page).to have_content("All API requests require authentication") + .and have_content("Contents") + .and have_link("Users") + .and have_link("Rake tasks") + .and have_header_with_correct_id_and_link(1, "GitLab API", "gitlab-api") + + click_link("Users") + + expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/users.md")) + expect(page).to have_content("Get a list of users.") + + page.go_back + + click_link("Rake tasks") + + expect(current_path).to eq(project_tree_path(project, "markdown/doc/raketasks")) + expect(page).to have_content("backup_restore.md").and have_content("maintenance.md") + + click_link("shop") + click_link("Maintenance") + + expect(current_path).to eq(project_blob_path(project, "markdown/doc/raketasks/maintenance.md")) + expect(page).to have_content("bundle exec rake gitlab:env:info RAILS_ENV=production") + + click_link("shop") + + page.within(".tree-table") do + click_link("README.md") + end + + page.go_back + + page.within(".tree-table") do + click_link("d") + end + + # rubocop:disable Lint/Void + # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`. + find("a", text: /^empty$/)["href"] == project_tree_url(project, "markdown/d") + # rubocop:enable Lint/Void + + page.within(".tree-table") do + click_link("README.md") + end + + # rubocop:disable Lint/Void + # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`. + find("a", text: /^empty$/)["href"] == project_blob_url(project, "markdown/d/README.md") + # rubocop:enable Lint/Void + end + + it "shows correct content of directory" do + click_link("GitLab API doc directory") + + expect(current_path).to eq(project_tree_path(project, "markdown/doc/api")) + expect(page).to have_content("README.md").and have_content("users.md") + + click_link("Users") + + expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/users.md")) + expect(page).to have_content("List users").and have_content("Get a list of users.") + end + end + end + + context "when browsing a specific ref" do + let(:ref) { project_tree_path(project, "6d39438") } + before do - visit(tree_path_ref_6d39438) + visit(ref) end - it 'shows files from a repository for "6d39438"' do - expect(current_path).to eq(tree_path_ref_6d39438) - expect(page).to have_content('.gitignore') - expect(page).to have_content('LICENSE') + it "shows files from a repository for `6d39438`" do + expect(current_path).to eq(ref) + expect(page).to have_content(".gitignore").and have_content("LICENSE") end - it 'shows files from a repository with apostroph in its name', :js do - first('.js-project-refs-dropdown').click + it "shows files from a repository with apostroph in its name", :js do + first(".js-project-refs-dropdown").click - page.within('.project-refs-form') do + page.within(".project-refs-form") do click_link("'test'") end - expect(page).to have_selector('.dropdown-toggle-text', text: "'test'") + expect(page).to have_selector(".dropdown-toggle-text", text: "'test'") visit(project_tree_path(project, "'test'")) - expect(page).to have_css('.tree-commit-link', visible: true) - expect(page).not_to have_content('Loading commit data...') + expect(page).to have_css(".tree-commit-link").and have_no_content("Loading commit data...") end - it 'shows the code with a leading dot in the directory', :js do - first('.js-project-refs-dropdown').click + it "shows the code with a leading dot in the directory", :js do + first(".js-project-refs-dropdown").click - page.within('.project-refs-form') do - click_link('fix') + page.within(".project-refs-form") do + click_link("fix") end - visit(project_tree_path(project, 'fix/.testdir')) + visit(project_tree_path(project, "fix/.testdir")) - expect(page).to have_css('.tree-commit-link', visible: true) - expect(page).not_to have_content('Loading commit data...') + expect(page).to have_css(".tree-commit-link").and have_no_content("Loading commit data...") end - it 'does not show the permalink link' do - click_link('.gitignore') + it "does not show the permalink link" do + click_link(".gitignore") - expect(page).not_to have_link('permalink') + expect(page).not_to have_link("permalink") end end - context 'when browsing a file content' do + context "when browsing a file content" do before do visit(tree_path_root_ref) - click_link('.gitignore') + + click_link(".gitignore") end - it 'shows a file content', :js do - wait_for_requests - expect(page).to have_content('*.rbc') + it "shows a file content", :js do + expect(page).to have_content("*.rbc") end - it 'is possible to blame' do - click_link 'Blame' + it "is possible to blame" do + click_link("Blame") - expect(page).to have_content "*.rb" - expect(page).to have_content "Dmitriy Zaporozhets" - expect(page).to have_content "Initial commit" + expect(page).to have_content("*.rb") + .and have_content("Dmitriy Zaporozhets") + .and have_content("Initial commit") end end - context 'when browsing a raw file' do + context "when browsing a raw file" do before do - visit(project_blob_path(project, File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path))) + path = File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path) + + visit(project_blob_path(project, path)) end - it 'shows a raw file content' do - click_link('Open raw') - expect(source).to eq('') # Body is filled in by gitlab-workhorse + it "shows a raw file content" do + click_link("Open raw") + + expect(source).to eq("") # Body is filled in by gitlab-workhorse end end end diff --git a/spec/features/projects/settings/lfs_settings_spec.rb b/spec/features/projects/settings/lfs_settings_spec.rb index 0fd28a5681c..342be1d2a9d 100644 --- a/spec/features/projects/settings/lfs_settings_spec.rb +++ b/spec/features/projects/settings/lfs_settings_spec.rb @@ -1,21 +1,27 @@ require 'rails_helper' describe 'Projects > Settings > LFS settings' do - let(:admin) { create(:admin) } let(:project) { create(:project) } + let(:user) { create(:user) } + let(:role) { :master } context 'LFS enabled setting' do before do allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) - sign_in(admin) + sign_in(user) + project.add_role(user, role) end - it 'displays the correct elements', :js do - visit edit_project_path(project) + context 'for master' do + let(:role) { :master } - expect(page).to have_content('Git Large File Storage') - expect(page).to have_selector('input[name="project[lfs_enabled]"] + button', visible: true) + it 'displays the correct elements', :js do + visit edit_project_path(project) + + expect(page).to have_content('Git Large File Storage') + expect(page).to have_selector('input[name="project[lfs_enabled]"] + button', visible: true) + end end end end diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index 4a9d1cb87e1..fe6fa55fa75 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -1,6 +1,6 @@ -require 'spec_helper' +require "spec_helper" -describe 'User creates wiki page' do +describe "User creates wiki page" do let(:user) { create(:user) } before do @@ -10,67 +10,104 @@ describe 'User creates wiki page' do visit(project_wikis_path(project)) end - context 'when wiki is empty' do - context 'in a user namespace' do + context "when wiki is empty" do + context "in a user namespace" do let(:project) { create(:project, namespace: user.namespace) } - it 'shows validation error message' do - page.within('.wiki-form') do - fill_in(:wiki_content, with: '') - click_on('Create page') + it "shows validation error message" do + page.within(".wiki-form") do + fill_in(:wiki_content, with: "") + + click_on("Create page") end - expect(page).to have_content('The form contains the following error:') - expect(page).to have_content("Content can't be blank") + expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank") + + page.within(".wiki-form") do + fill_in(:wiki_content, with: "[link test](test)") - page.within('.wiki-form') do - fill_in(:wiki_content, with: '[link test](test)') - click_on('Create page') + click_on("Create page") end - expect(page).to have_content('Home') - expect(page).to have_content('link test') + expect(page).to have_content("Home").and have_content("link test") - click_link('link test') + click_link("link test") - expect(page).to have_content('Create Page') + expect(page).to have_content("Create Page") end - it 'shows non-escaped link in the pages list', :js do - click_link('New page') + it "shows non-escaped link in the pages list", :js do + click_link("New page") - page.within('#modal-new-wiki') do - fill_in(:new_wiki_path, with: 'one/two/three-test') - click_on('Create page') + page.within("#modal-new-wiki") do + fill_in(:new_wiki_path, with: "one/two/three-test") + + click_on("Create page") end - page.within('.wiki-form') do - fill_in(:wiki_content, with: 'wiki content') - click_on('Create page') + page.within(".wiki-form") do + fill_in(:wiki_content, with: "wiki content") + + click_on("Create page") end - expect(current_path).to include('one/two/three-test') + expect(current_path).to include("one/two/three-test") expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']") end - it 'has "Create home" as a commit message' do - expect(page).to have_field('wiki[message]', with: 'Create home') + it "has `Create home` as a commit message" do + expect(page).to have_field("wiki[message]", with: "Create home") end - it 'creates a page from the home page' do - fill_in(:wiki_content, with: 'My awesome wiki!') + it "creates a page from the home page" do + fill_in(:wiki_content, with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n# Wiki header\n") + fill_in(:wiki_message, with: "Adding links to wiki") + + page.within(".wiki-form") do + click_button("Create page") + end + + expect(current_path).to eq(project_wiki_path(project, "home")) + expect(page).to have_content("test GitLab API doc Rake tasks Wiki header") + .and have_content("Home") + .and have_content("Last edited by #{user.name}") + .and have_header_with_correct_id_and_link(1, "Wiki header", "wiki-header") + + click_link("test") - page.within('.wiki-form') do - click_button('Create page') + expect(current_path).to eq(project_wiki_path(project, "test")) + + page.within(:css, ".nav-text") do + expect(page).to have_content("Test").and have_content("Create Page") + end + + click_link("Home") + + expect(current_path).to eq(project_wiki_path(project, "home")) + + click_link("GitLab API") + + expect(current_path).to eq(project_wiki_path(project, "api")) + + page.within(:css, ".nav-text") do + expect(page).to have_content("Create").and have_content("Api") end - expect(page).to have_content('Home') - expect(page).to have_content("Last edited by #{user.name}") - expect(page).to have_content('My awesome wiki!') + click_link("Home") + + expect(current_path).to eq(project_wiki_path(project, "home")) + + click_link("Rake tasks") + + expect(current_path).to eq(project_wiki_path(project, "raketasks")) + + page.within(:css, ".nav-text") do + expect(page).to have_content("Create").and have_content("Rake") + end end - it 'creates ASCII wiki with LaTeX blocks', :js do - stub_application_setting(plantuml_url: 'http://localhost', plantuml_enabled: true) + it "creates ASCII wiki with LaTeX blocks", :js do + stub_application_setting(plantuml_url: "http://localhost", plantuml_enabled: true) ascii_content = <<~MD :stem: latexmath @@ -90,153 +127,164 @@ describe 'User creates wiki page' do stem:[2+2] is 4 MD - find('#wiki_format option[value=asciidoc]').select_option + find("#wiki_format option[value=asciidoc]").select_option + fill_in(:wiki_content, with: ascii_content) - page.within('.wiki-form') do - click_button('Create page') + page.within(".wiki-form") do + click_button("Create page") end - page.within '.wiki' do - expect(page).to have_selector('.katex', count: 3) - expect(page).to have_content('2+2 is 4') + page.within ".wiki" do + expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4") end end end - context 'in a group namespace', :js do + context "in a group namespace", :js do let(:project) { create(:project, namespace: create(:group, :public)) } - it 'has "Create home" as a commit message' do - expect(page).to have_field('wiki[message]', with: 'Create home') + it "has `Create home` as a commit message" do + expect(page).to have_field("wiki[message]", with: "Create home") end - it 'creates a page from from the home page' do - page.within('.wiki-form') do - fill_in(:wiki_content, with: 'My awesome wiki!') - click_button('Create page') + it "creates a page from from the home page" do + page.within(".wiki-form") do + fill_in(:wiki_content, with: "My awesome wiki!") + + click_button("Create page") end - expect(page).to have_content('Home') - expect(page).to have_content("Last edited by #{user.name}") - expect(page).to have_content('My awesome wiki!') + expect(page).to have_content("Home") + .and have_content("Last edited by #{user.name}") + .and have_content("My awesome wiki!") end end end - context 'when wiki is not empty', :js do + context "when wiki is not empty", :js do before do - create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: 'home', content: 'Home page' }) + create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: "home", content: "Home page" }) end - context 'in a user namespace' do + context "in a user namespace" do let(:project) { create(:project, namespace: user.namespace) } - context 'via the "new wiki page" page' do - it 'creates a page with a single word' do - click_link('New page') + context "via the `new wiki page` page" do + it "creates a page with a single word" do + click_link("New page") - page.within('#modal-new-wiki') do - fill_in(:new_wiki_path, with: 'foo') - click_button('Create page') + page.within("#modal-new-wiki") do + fill_in(:new_wiki_path, with: "foo") + + click_button("Create page") end # Commit message field should have correct value. - expect(page).to have_field('wiki[message]', with: 'Create foo') + expect(page).to have_field("wiki[message]", with: "Create foo") + + page.within(".wiki-form") do + fill_in(:wiki_content, with: "My awesome wiki!") - page.within('.wiki-form') do - fill_in(:wiki_content, with: 'My awesome wiki!') - click_button('Create page') + click_button("Create page") end - expect(page).to have_content('Foo') - expect(page).to have_content("Last edited by #{user.name}") - expect(page).to have_content('My awesome wiki!') + expect(page).to have_content("Foo") + .and have_content("Last edited by #{user.name}") + .and have_content("My awesome wiki!") end - it 'creates a page with spaces in the name' do - click_link('New page') + it "creates a page with spaces in the name" do + click_link("New page") - page.within('#modal-new-wiki') do - fill_in(:new_wiki_path, with: 'Spaces in the name') - click_button('Create page') + page.within("#modal-new-wiki") do + fill_in(:new_wiki_path, with: "Spaces in the name") + + click_button("Create page") end # Commit message field should have correct value. - expect(page).to have_field('wiki[message]', with: 'Create spaces in the name') + expect(page).to have_field("wiki[message]", with: "Create spaces in the name") + + page.within(".wiki-form") do + fill_in(:wiki_content, with: "My awesome wiki!") - page.within('.wiki-form') do - fill_in(:wiki_content, with: 'My awesome wiki!') - click_button('Create page') + click_button("Create page") end - expect(page).to have_content('Spaces in the name') - expect(page).to have_content("Last edited by #{user.name}") - expect(page).to have_content('My awesome wiki!') + expect(page).to have_content("Spaces in the name") + .and have_content("Last edited by #{user.name}") + .and have_content("My awesome wiki!") end - it 'creates a page with hyphens in the name' do - click_link('New page') + it "creates a page with hyphens in the name" do + click_link("New page") - page.within('#modal-new-wiki') do - fill_in(:new_wiki_path, with: 'hyphens-in-the-name') - click_button('Create page') + page.within("#modal-new-wiki") do + fill_in(:new_wiki_path, with: "hyphens-in-the-name") + + click_button("Create page") end # Commit message field should have correct value. - expect(page).to have_field('wiki[message]', with: 'Create hyphens in the name') + expect(page).to have_field("wiki[message]", with: "Create hyphens in the name") + + page.within(".wiki-form") do + fill_in(:wiki_content, with: "My awesome wiki!") - page.within('.wiki-form') do - fill_in(:wiki_content, with: 'My awesome wiki!') - click_button('Create page') + click_button("Create page") end - expect(page).to have_content('Hyphens in the name') - expect(page).to have_content("Last edited by #{user.name}") - expect(page).to have_content('My awesome wiki!') + expect(page).to have_content("Hyphens in the name") + .and have_content("Last edited by #{user.name}") + .and have_content("My awesome wiki!") end end - it 'shows the autocompletion dropdown' do - click_link('New page') + it "shows the autocompletion dropdown" do + click_link("New page") - page.within('#modal-new-wiki') do - fill_in(:new_wiki_path, with: 'test-autocomplete') - click_button('Create page') + page.within("#modal-new-wiki") do + fill_in(:new_wiki_path, with: "test-autocomplete") + + click_button("Create page") end - page.within('.wiki-form') do - find('#wiki_content').native.send_keys('') - fill_in(:wiki_content, with: '@') + page.within(".wiki-form") do + find("#wiki_content").native.send_keys("") + + fill_in(:wiki_content, with: "@") end - expect(page).to have_selector('.atwho-view') + expect(page).to have_selector(".atwho-view") end end - context 'in a group namespace' do + context "in a group namespace" do let(:project) { create(:project, namespace: create(:group, :public)) } - context 'via the "new wiki page" page' do - it 'creates a page' do - click_link('New page') + context "via the `new wiki page` page" do + it "creates a page" do + click_link("New page") - page.within('#modal-new-wiki') do - fill_in(:new_wiki_path, with: 'foo') - click_button('Create page') + page.within("#modal-new-wiki") do + fill_in(:new_wiki_path, with: "foo") + + click_button("Create page") end # Commit message field should have correct value. - expect(page).to have_field('wiki[message]', with: 'Create foo') + expect(page).to have_field("wiki[message]", with: "Create foo") + + page.within(".wiki-form") do + fill_in(:wiki_content, with: "My awesome wiki!") - page.within('.wiki-form') do - fill_in(:wiki_content, with: 'My awesome wiki!') - click_button('Create page') + click_button("Create page") end - expect(page).to have_content('Foo') - expect(page).to have_content("Last edited by #{user.name}") - expect(page).to have_content('My awesome wiki!') + expect(page).to have_content("Foo") + .and have_content("Last edited by #{user.name}") + .and have_content("My awesome wiki!") end end end diff --git a/spec/features/users/active_sessions_spec.rb b/spec/features/users/active_sessions_spec.rb new file mode 100644 index 00000000000..631d7e3bced --- /dev/null +++ b/spec/features/users/active_sessions_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +feature 'Active user sessions', :clean_gitlab_redis_shared_state do + scenario 'Successful login adds a new active user login' do + now = Time.zone.parse('2018-03-12 09:06') + Timecop.freeze(now) do + user = create(:user) + gitlab_sign_in(user) + expect(current_path).to eq root_path + + sessions = ActiveSession.list(user) + expect(sessions.count).to eq 1 + + # refresh the current page updates the updated_at + Timecop.freeze(now + 1.minute) do + visit current_path + + sessions = ActiveSession.list(user) + expect(sessions.first).to have_attributes( + created_at: Time.zone.parse('2018-03-12 09:06'), + updated_at: Time.zone.parse('2018-03-12 09:07') + ) + end + end + end + + scenario 'Successful login cleans up obsolete entries' do + user = create(:user) + + Gitlab::Redis::SharedState.with do |redis| + redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') + end + + gitlab_sign_in(user) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d' + end + end + + scenario 'Sessionless login does not clean up obsolete entries' do + user = create(:user) + personal_access_token = create(:personal_access_token, user: user) + + Gitlab::Redis::SharedState.with do |redis| + redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') + end + + visit user_path(user, :atom, private_token: personal_access_token.token) + expect(page.status_code).to eq 200 + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d' + end + end + + scenario 'Logout deletes the active user login' do + user = create(:user) + gitlab_sign_in(user) + expect(current_path).to eq root_path + + expect(ActiveSession.list(user).count).to eq 1 + + gitlab_sign_out + expect(current_path).to eq new_user_session_path + + expect(ActiveSession.list(user)).to be_empty + end +end diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js index 98ab61a0367..cea603368bf 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js @@ -1,9 +1,9 @@ import Vue from 'vue'; -import wipComponent from '~/vue_merge_request_widget/components/states/mr_widget_wip'; +import WorkInProgress from '~/vue_merge_request_widget/components/states/work_in_progress.vue'; import eventHub from '~/vue_merge_request_widget/event_hub'; const createComponent = () => { - const Component = Vue.extend(wipComponent); + const Component = Vue.extend(WorkInProgress); const mr = { title: 'The best MR ever', removeWIPPath: '/path/to/remove/wip', @@ -17,10 +17,10 @@ const createComponent = () => { }); }; -describe('MRWidgetWIP', () => { +describe('Wip', () => { describe('props', () => { it('should have props', () => { - const { mr, service } = wipComponent.props; + const { mr, service } = WorkInProgress.props; expect(mr.type instanceof Object).toBeTruthy(); expect(mr.required).toBeTruthy(); diff --git a/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb new file mode 100644 index 00000000000..f8107dd40b9 --- /dev/null +++ b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe Gitlab::BackgroundMigration::MigrateStageIndex, :migration, schema: 20180420080616 do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:pipelines) { table(:ci_pipelines) } + let(:stages) { table(:ci_stages) } + let(:jobs) { table(:ci_builds) } + + before do + namespaces.create(id: 10, name: 'gitlab-org', path: 'gitlab-org') + projects.create!(id: 11, namespace_id: 10, name: 'gitlab', path: 'gitlab') + pipelines.create!(id: 12, project_id: 11, ref: 'master', sha: 'adf43c3a') + + stages.create(id: 100, project_id: 11, pipeline_id: 12, name: 'build') + stages.create(id: 101, project_id: 11, pipeline_id: 12, name: 'test') + + jobs.create!(id: 121, commit_id: 12, project_id: 11, + stage_idx: 2, stage_id: 100) + jobs.create!(id: 122, commit_id: 12, project_id: 11, + stage_idx: 2, stage_id: 100) + jobs.create!(id: 123, commit_id: 12, project_id: 11, + stage_idx: 10, stage_id: 100) + jobs.create!(id: 124, commit_id: 12, project_id: 11, + stage_idx: 3, stage_id: 101) + end + + it 'correctly migrates stages indices' do + expect(stages.all.pluck(:position)).to all(be_nil) + + described_class.new.perform(100, 101) + + expect(stages.all.pluck(:position)).to eq [2, 3] + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb index dc12ba076bc..0edc3f315bb 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb @@ -17,7 +17,7 @@ describe Gitlab::Ci::Pipeline::Chain::Create do context 'when pipeline is ready to be saved' do before do - pipeline.stages.build(name: 'test', project: project) + pipeline.stages.build(name: 'test', position: 0, project: project) step.perform! end diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb index eb1b285c7bd..05ce3412fd8 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb @@ -24,7 +24,8 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do describe '#attributes' do it 'returns hash attributes of a stage' do expect(subject.attributes).to be_a Hash - expect(subject.attributes).to include(:name, :project) + expect(subject.attributes) + .to include(:name, :position, :pipeline, :project) end end diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 31141807cb2..62da967cf96 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -232,6 +232,7 @@ Ci::Stage: - id - name - status +- position - lock_version - project_id - pipeline_id diff --git a/spec/migrations/schedule_stages_index_migration_spec.rb b/spec/migrations/schedule_stages_index_migration_spec.rb new file mode 100644 index 00000000000..710264da375 --- /dev/null +++ b/spec/migrations/schedule_stages_index_migration_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20180420080616_schedule_stages_index_migration') + +describe ScheduleStagesIndexMigration, :sidekiq, :migration do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:pipelines) { table(:ci_pipelines) } + let(:stages) { table(:ci_stages) } + + before do + stub_const("#{described_class}::BATCH_SIZE", 1) + + namespaces.create(id: 12, name: 'gitlab-org', path: 'gitlab-org') + projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab') + pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a') + stages.create!(id: 121, project_id: 123, pipeline_id: 1, name: 'build') + stages.create!(id: 122, project_id: 123, pipeline_id: 1, name: 'test') + stages.create!(id: 123, project_id: 123, pipeline_id: 1, name: 'deploy') + end + + it 'schedules delayed background migrations in batches' do + Sidekiq::Testing.fake! do + Timecop.freeze do + expect(stages.all).to all(have_attributes(position: be_nil)) + + migrate! + + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 121, 121) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 122, 122) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, 123, 123) + expect(BackgroundMigrationWorker.jobs.size).to eq 3 + end + end + end +end diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb new file mode 100644 index 00000000000..129b2f92683 --- /dev/null +++ b/spec/models/active_session_spec.rb @@ -0,0 +1,216 @@ +require 'rails_helper' + +RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do + let(:user) do + create(:user).tap do |user| + user.current_sign_in_at = Time.current + end + end + + let(:session) { double(:session, id: '6919a6f1bb119dd7396fadc38fd18d0d') } + + let(:request) do + double(:request, { + user_agent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 ' \ + '(KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]', + ip: '127.0.0.1', + session: session + }) + end + + describe '#current?' do + it 'returns true if the active session matches the current session' do + active_session = ActiveSession.new(session_id: '6919a6f1bb119dd7396fadc38fd18d0d') + + expect(active_session.current?(session)).to be true + end + + it 'returns false if the active session does not match the current session' do + active_session = ActiveSession.new(session_id: '59822c7d9fcdfa03725eff41782ad97d') + + expect(active_session.current?(session)).to be false + end + + it 'returns false if the session id is nil' do + active_session = ActiveSession.new(session_id: nil) + session = double(:session, id: nil) + + expect(active_session.current?(session)).to be false + end + end + + describe '.list' do + it 'returns all sessions by user' do + Gitlab::Redis::SharedState.with do |redis| + redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' })) + redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", Marshal.dump({ session_id: 'b' })) + redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '') + + redis.sadd( + "session:lookup:user:gitlab:#{user.id}", + %w[ + 6919a6f1bb119dd7396fadc38fd18d0d + 59822c7d9fcdfa03725eff41782ad97d + ] + ) + end + + expect(ActiveSession.list(user)).to match_array [{ session_id: 'a' }, { session_id: 'b' }] + end + + it 'does not return obsolete entries and cleans them up' do + Gitlab::Redis::SharedState.with do |redis| + redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' })) + + redis.sadd( + "session:lookup:user:gitlab:#{user.id}", + %w[ + 6919a6f1bb119dd7396fadc38fd18d0d + 59822c7d9fcdfa03725eff41782ad97d + ] + ) + end + + expect(ActiveSession.list(user)).to eq [{ session_id: 'a' }] + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.sscan_each("session:lookup:user:gitlab:#{user.id}").to_a).to eq ['6919a6f1bb119dd7396fadc38fd18d0d'] + end + end + + it 'returns an empty array if the use does not have any active session' do + expect(ActiveSession.list(user)).to eq [] + end + end + + describe '.set' do + it 'sets a new redis entry for the user session and a lookup entry' do + ActiveSession.set(user, request) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.scan_each.to_a).to match_array [ + "session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", + "session:lookup:user:gitlab:#{user.id}" + ] + end + end + + it 'adds timestamps and information from the request' do + Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do + ActiveSession.set(user, request) + + session = ActiveSession.list(user) + + expect(session.count).to eq 1 + expect(session.first).to have_attributes( + ip_address: '127.0.0.1', + browser: 'Mobile Safari', + os: 'iOS', + device_name: 'iPhone 6', + device_type: 'smartphone', + created_at: Time.zone.parse('2018-03-12 09:06'), + updated_at: Time.zone.parse('2018-03-12 09:06'), + session_id: '6919a6f1bb119dd7396fadc38fd18d0d' + ) + end + end + + it 'keeps the created_at from the login on consecutive requests' do + now = Time.zone.parse('2018-03-12 09:06') + + Timecop.freeze(now) do + ActiveSession.set(user, request) + + Timecop.freeze(now + 1.minute) do + ActiveSession.set(user, request) + + session = ActiveSession.list(user) + + expect(session.first).to have_attributes( + created_at: Time.zone.parse('2018-03-12 09:06'), + updated_at: Time.zone.parse('2018-03-12 09:07') + ) + end + end + end + end + + describe '.destroy' do + it 'removes the entry associated with the currently killed user session' do + Gitlab::Redis::SharedState.with do |redis| + redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') + redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", '') + redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '') + end + + ActiveSession.destroy(user, request.session.id) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.scan_each(match: "session:user:gitlab:*")).to match_array [ + "session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", + "session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358" + ] + end + end + + it 'removes the lookup entry' do + Gitlab::Redis::SharedState.with do |redis| + redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') + redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d') + end + + ActiveSession.destroy(user, request.session.id) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty + end + end + + it 'removes the devise session' do + Gitlab::Redis::SharedState.with do |redis| + redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') + redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '') + end + + ActiveSession.destroy(user, request.session.id) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty + end + end + + it 'does not remove the devise session if the active session could not be found' do + Gitlab::Redis::SharedState.with do |redis| + redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '') + end + + other_user = create(:user) + + ActiveSession.destroy(other_user, request.session.id) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.scan_each(match: "session:gitlab:*").to_a).not_to be_empty + end + end + end + + describe '.cleanup' do + it 'removes obsolete lookup entries' do + Gitlab::Redis::SharedState.with do |redis| + redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') + redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d') + redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') + end + + ActiveSession.cleanup(user) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq ['6919a6f1bb119dd7396fadc38fd18d0d'] + end + end + + it 'does not bail if there are no lookup entries' do + ActiveSession.cleanup(user) + end + end +end diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index 586d073eb5e..a00db1d2bfc 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -51,7 +51,7 @@ describe Ci::Stage, :models do end end - describe 'update_status' do + describe '#update_status' do context 'when stage objects needs to be updated' do before do create(:ci_build, :success, stage_id: stage.id) @@ -87,4 +87,36 @@ describe Ci::Stage, :models do end end end + + describe '#index' do + context 'when stage has been imported and does not have position index set' do + before do + stage.update_column(:position, nil) + end + + context 'when stage has statuses' do + before do + create(:ci_build, :running, stage_id: stage.id, stage_idx: 10) + end + + it 'recalculates index before updating status' do + expect(stage.reload.position).to be_nil + + stage.update_status + + expect(stage.reload.position).to eq 10 + end + end + + context 'when stage does not have statuses' do + it 'fallbacks to zero' do + expect(stage.reload.position).to be_nil + + stage.update_status + + expect(stage.reload.position).to eq 0 + end + end + end + end end diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb index ba06ff42d87..6e35511e848 100644 --- a/spec/models/lfs_object_spec.rb +++ b/spec/models/lfs_object_spec.rb @@ -62,9 +62,7 @@ describe LfsObject do .with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric)) .once - lfs_object = create(:lfs_object) - lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") - lfs_object.save! + create(:lfs_object, :with_file) end end end diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb index 2a0d102d3fe..12681a147b4 100644 --- a/spec/models/notification_setting_spec.rb +++ b/spec/models/notification_setting_spec.rb @@ -40,7 +40,12 @@ RSpec.describe NotificationSetting do expect(notification_setting.new_issue).to eq(true) expect(notification_setting.close_issue).to eq(true) expect(notification_setting.merge_merge_request).to eq(true) - expect(notification_setting.close_merge_request).to eq(false) + + # In Rails 5 assigning a value which is not explicitly `true` or `false` ("nil" in this case) + # to a boolean column transforms it to `true`. + # In Rails 4 it transforms the value to `false` with deprecation warning. + # Replace `eq(Gitlab.rails5?)` with `eq(true)` when removing rails5? code. + expect(notification_setting.close_merge_request).to eq(Gitlab.rails5?) expect(notification_setting.reopen_merge_request).to eq(false) end end diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index 3ffdfdc0e9a..0a2963452e4 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -281,7 +281,7 @@ describe API::Jobs do get_artifact_file(artifact) expect(response).to have_gitlab_http_status(200) - expect(response.headers) + expect(response.headers.to_h) .to include('Content-Type' => 'application/json', 'Gitlab-Workhorse-Send-Data' => /artifacts-entry/) end @@ -311,7 +311,7 @@ describe API::Jobs do it 'returns specific job artifacts' do expect(response).to have_gitlab_http_status(200) - expect(response.headers).to include(download_headers) + expect(response.headers.to_h).to include(download_headers) expect(response.body).to match_file(job.artifacts_file.file.file) end end @@ -462,7 +462,7 @@ describe API::Jobs do end it { expect(response).to have_http_status(:ok) } - it { expect(response.headers).to include(download_headers) } + it { expect(response.headers.to_h).to include(download_headers) } end context 'when artifacts are stored remotely' do diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index d9c33a7b8ab..f27c95b4907 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -1355,7 +1355,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do it 'download artifacts' do expect(response).to have_http_status(200) - expect(response.headers).to include download_headers + expect(response.headers.to_h).to include download_headers end end @@ -1370,7 +1370,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do it 'uses workhorse send-url' do expect(response).to have_gitlab_http_status(200) - expect(response.headers).to include( + expect(response.headers.to_h).to include( 'Gitlab-Workhorse-Send-Data' => /send-url:/) end end diff --git a/spec/requests/api/v3/builds_spec.rb b/spec/requests/api/v3/builds_spec.rb index 00f067889a0..485d7c2cc43 100644 --- a/spec/requests/api/v3/builds_spec.rb +++ b/spec/requests/api/v3/builds_spec.rb @@ -232,7 +232,7 @@ describe API::V3::Builds do it 'returns specific job artifacts' do expect(response).to have_http_status(200) - expect(response.headers).to include(download_headers) + expect(response.headers.to_h).to include(download_headers) expect(response.body).to match_file(build.artifacts_file.file.file) end end @@ -332,7 +332,7 @@ describe API::V3::Builds do end it { expect(response).to have_http_status(200) } - it { expect(response.headers).to include(download_headers) } + it { expect(response.headers.to_h).to include(download_headers) } end context 'when artifacts are stored remotely' do diff --git a/spec/services/applications/create_service_spec.rb b/spec/services/applications/create_service_spec.rb index 47a2a9d6403..9c43b56744b 100644 --- a/spec/services/applications/create_service_spec.rb +++ b/spec/services/applications/create_service_spec.rb @@ -1,13 +1,17 @@ -require 'spec_helper' +require "spec_helper" describe ::Applications::CreateService do let(:user) { create(:user) } let(:params) { attributes_for(:application) } - let(:request) { ActionController::TestRequest.new(remote_ip: '127.0.0.1') } + let(:request) do + if Gitlab.rails5? + ActionController::TestRequest.new({ remote_ip: "127.0.0.1" }, ActionController::TestSession.new) + else + ActionController::TestRequest.new(remote_ip: "127.0.0.1") + end + end subject { described_class.new(user, params) } - it 'creates an application' do - expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1) - end + it { expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1) } end diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index cc200f6fc0a..e1cb7ed8110 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -6,7 +6,9 @@ describe Ci::RetryBuildService do set(:pipeline) { create(:ci_pipeline, project: project) } let(:stage) do - Ci::Stage.create!(project: project, pipeline: pipeline, name: 'test') + create(:ci_stage_entity, project: project, + pipeline: pipeline, + name: 'test') end let(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) } diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index c38ddf4612b..e8568bf8bb3 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -219,7 +219,7 @@ describe MergeRequests::MergeService do service.execute(merge_request) - expect(merge_request.merge_error).to include(error_message) + expect(merge_request.merge_error).to include('Something went wrong during merge') expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message)) end @@ -231,7 +231,7 @@ describe MergeRequests::MergeService do service.execute(merge_request) - expect(merge_request.merge_error).to include(error_message) + expect(merge_request.merge_error).to include('Something went wrong during merge pre-receive hook') expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message)) end diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index a418808fd26..347ac13828c 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -123,11 +123,13 @@ describe Projects::UpdatePagesService do expect(execute).not_to eq(:success) end - it 'fails for empty file fails' do - build.job_artifacts_archive.update_attributes(file: empty_file) + context 'when using empty file' do + let(:file) { empty_file } - expect { execute } - .to raise_error(Projects::UpdatePagesService::FailedToExtractError) + it 'fails to extract' do + expect { execute } + .to raise_error(Projects::UpdatePagesService::FailedToExtractError) + end end context 'when timeout happens by DNS error' do diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb index 5b0b609f7f2..5a569d233bc 100644 --- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb +++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb @@ -79,7 +79,7 @@ RSpec.shared_examples 'a creatable merge request' do end end - it 'updates the branches when selecting a new target project' do + it 'updates the branches when selecting a new target project', :js do target_project_member = target_project.owner CreateBranchService.new(target_project, target_project_member) .execute('a-brand-new-branch-to-test', 'master') @@ -92,7 +92,7 @@ RSpec.shared_examples 'a creatable merge request' do first('.js-target-branch').click - within('.dropdown-target-branch .dropdown-content') do + within('.js-target-branch-dropdown .dropdown-content') do expect(page).to have_content('a-brand-new-branch-to-test') end end diff --git a/spec/uploaders/lfs_object_uploader_spec.rb b/spec/uploaders/lfs_object_uploader_spec.rb index a2fb3886610..9f28510c3e4 100644 --- a/spec/uploaders/lfs_object_uploader_spec.rb +++ b/spec/uploaders/lfs_object_uploader_spec.rb @@ -46,8 +46,7 @@ describe LfsObjectUploader do end describe 'remote file' do - let(:remote) { described_class::Store::REMOTE } - let(:lfs_object) { create(:lfs_object, file_store: remote) } + let(:lfs_object) { create(:lfs_object, :object_storage, :with_file) } context 'with object storage enabled' do before do @@ -57,16 +56,11 @@ describe LfsObjectUploader do it 'can store file remotely' do allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async) - store_file(lfs_object) + lfs_object - expect(lfs_object.file_store).to eq remote + expect(lfs_object.file_store).to eq(described_class::Store::REMOTE) expect(lfs_object.file.path).not_to be_blank end end end - - def store_file(lfs_object) - lfs_object.file = fixture_file_upload(Rails.root.join("spec/fixtures/dk.png"), "`/png") - lfs_object.save! - end end |