├── .babelrc
├── .editorconfig
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .yo-rc.json
├── LICENSE
├── README.md
├── cfg
├── base.js
├── defaults.js
├── dev.js
├── dist.js
└── test.js
├── dist
├── README.md
├── assets
│ ├── app.js
│ ├── app.js.map
│ ├── emoji.css
│ └── emoji.png
├── ga.js
└── index.html
├── karma.conf.js
├── package.json
├── screenshot.png
├── server.js
├── src
├── actions
│ └── index.js
├── components
│ ├── App.js
│ ├── ConfigBar.js
│ ├── ConfigPanel.js
│ ├── ControlBar.js
│ ├── Grid.js
│ └── Weeper.js
├── containers
│ ├── ConfigBar.js
│ ├── ConfigPanel.js
│ ├── ControlBar.js
│ ├── Grid.js
│ └── Weeper.js
├── emoji.css
├── emoji.png
├── hasAppleColorEmoji.js
├── index.html
├── index.js
├── minesweeper.js
├── reducers
│ └── index.js
├── rref.js
└── test.js
├── test
├── actions
│ └── .keep
├── components
│ └── MainTest.js
├── config
│ └── ConfigTest.js
├── helpers
│ └── shallowRenderHelper.js
├── loadtests.js
├── sources
│ └── .keep
└── stores
│ └── .keep
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react",
5 | "stage-2"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | indent_size = 4
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "node": true
6 | },
7 | "extends": "eslint:recommended",
8 | "parserOptions": {
9 | "ecmaFeatures": {
10 | "experimentalObjectRestSpread": true,
11 | "jsx": true
12 | },
13 | "sourceType": "module"
14 | },
15 | "plugins": [
16 | "react"
17 | ],
18 | "rules": {
19 | "indent": 0,
20 | "linebreak-style": [
21 | 1,
22 | "unix"
23 | ],
24 | "semi": [
25 | 1,
26 | "always"
27 | ],
28 | "quotes": 0,
29 | "no-console": 0
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | dist/* linguist-vendored
2 | *.css linguist-vendored
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | # Bower
30 | bower_components/
31 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-react-webpack": {
3 | "appName": "minesweeper",
4 | "style": "scss",
5 | "postcss": true,
6 | "generatedWithVersion": 3
7 | }
8 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Kai Hao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # minesweeper
2 | The most complete minesweeper game built with React, Redux and Immutable.js.
3 |
4 | [Play Now!](https://kevin940726.github.io/minesweeper)
5 |
6 | 
7 |
8 | ---
9 |
10 | ### Features
11 |
12 | - [x] Custom game board and three different major difficulty.
13 | - [x] Counting Time.
14 | - [x] Mines left.
15 | - [x] Set flags.
16 | - [x] Quick mode and mode switch.
17 | - [x] Settings save in cookie.
18 | - [x] Generate non-guessing game.
19 |
20 | ### How to Play
21 | Seriously !?
22 |
23 | ### Build
24 | git clone this project, then.
25 | ```bash
26 | cd minesweeper
27 | npm install
28 | npm start
29 | ```
30 | Head to [localhost:8000](localhost:8000).
31 |
32 | ### API
33 | You can find the logic of the game in `./src/minesweeper.js`. If you don't like the UI or you want to make a enhancement, import it to your custom UI.
34 |
35 | ```js
36 | import Minesweeper, { Block, BlockRecord } from './src/minesweeper.js';
37 | ```
38 |
39 | #### `Block`:
40 | An immutable Record store in each block. Check out [immutable.js](https://facebook.github.io/immutable-js/) for methods.
41 |
42 | ```js
43 | const Block = Record({
44 | type: 'normal',
45 | mines: 0,
46 | hidden: true,
47 | flag: false
48 | });
49 | ```
50 |
51 | #### `BlockRecord`:
52 | An immutable Record indicate the row and column of the block.
53 |
54 | ```js
55 | const BlockRecord = Record({
56 | row: 0,
57 | col: 0
58 | });
59 | ```
60 |
61 | #### `Blocks`:
62 | An immutable Map represent the whole game board blocks, with `BlockRecord` as keys and `Block` as values.
63 |
64 | #### `Minesweeper()`:
65 | Create the game board, load initial data. There are some game data you can get:
66 |
67 | - `rows`: _(Int)_ Game board rows.
68 | - `cols`: _(Int)_ Game board columns.
69 | - `mines`: _(Int)_ Game board mines.
70 | - `minesRemaining`: _(Int)_ How many mines left without flagged.
71 | - `blocks`: _(Immutable Map)_ An immutable Map data for each blocks in the game board.
72 | - `status`: _(String)_
73 | * `ready`: Ready to play the game before first click.
74 | * `playing`: Playing the game after first click.
75 | * `win`: Clear the game board without hitting mines.
76 | * `lose`: Hit a mine.
77 | - `timePass`: _(Int)_ Current time pass in the game.
78 | - `mode`: _(String)_
79 | * `regular`: Regular game mode. Single click to reveal the block, right click to set a flag.
80 | * `quick`: Quick mode. Single click to set a flag, while right click will reveal the block.
81 | - `flagMode`: _(Boolean)_ Quick mode for the game or not.
82 | - `checkIsSolvable`: _(Boolean)_ Generate non-guessing game or not.
83 |
84 | ```js
85 | const game = Minesweeper();
86 | ```
87 |
88 | #### `Minesweeper::reset(rows, cols, mines, quickMode)`:
89 | Clear the game board and reset the config settings. Return `blocks`.
90 |
91 | #### `Minesweeper::init(rows, cols, mines, quickMode, exclude)`:
92 | Reset and start the game. The `exclude` take an array of `BlockRecord` to exclude from being mine. Return `blocks`.
93 |
94 | #### `Minesweeper::singleClick(BlockRecord)`:
95 | Perform a single click on a block. Set a flag if `mode` === `quick`, else reveal the block. Return a Promise with `blocks` as argument.
96 |
97 | #### `Minesweeper::rightClick(BlockRecord)`:
98 | Perform a right click on a block. Reveal the block if `mode` === `quick`, else set a flag. Return a Promise with `blocks` as argument.
99 |
100 | ### TODO
101 |
102 | - [ ] Record high score.
103 | - [x] Cleverly first click init.
104 | - [ ] Mobile friendly.
105 | - [x] Game solver (_hard_, for non-guessing game).
106 | - [x] Completely switch to Immutable.js.
107 | - [ ] Increase solver speed.
108 | - [x] Cover up loading screen.
109 | - [ ] Write some tests.
110 | - [ ] Generate hints by solver.
111 |
112 | ### Contribute
113 | Issues, PRs, and all the advise and discussion are very welcome!
114 |
115 | ### Special Thanks
116 |
117 | - [muan/emoji](https://github.com/muan/emoji): Great cross-platform emoji collection!
118 | - [Solving Minesweeper with Matrices By Robert Massaioli](https://massaioli.wordpress.com/2013/01/12/solving-minesweeper-with-matricies/): Awesome and inspiring minesweeper solver algorithm.
119 |
120 | ### License
121 | [MIT](./LICENSE)
122 |
--------------------------------------------------------------------------------
/cfg/base.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let path = require('path');
3 | let defaultSettings = require('./defaults');
4 |
5 | // Additional npm or bower modules to include in builds
6 | // Add all foreign plugins you may need into this array
7 | // @example:
8 | // let npmBase = path.join(__dirname, '../node_modules');
9 | // let additionalPaths = [ path.join(npmBase, 'react-bootstrap') ];
10 | let additionalPaths = [];
11 |
12 | module.exports = {
13 | additionalPaths: additionalPaths,
14 | port: defaultSettings.port,
15 | debug: true,
16 | devtool: 'eval',
17 | output: {
18 | path: path.join(__dirname, '/../dist/assets'),
19 | filename: 'app.js',
20 | publicPath: `.${defaultSettings.publicPath}`
21 | },
22 | devServer: {
23 | contentBase: './src/',
24 | historyApiFallback: true,
25 | hot: true,
26 | port: defaultSettings.port,
27 | publicPath: defaultSettings.publicPath,
28 | quiet: true
29 | },
30 | resolve: {
31 | extensions: ['', '.js', '.jsx'],
32 | alias: {
33 | actions: `${defaultSettings.srcPath}/actions/`,
34 | components: `${defaultSettings.srcPath}/components/`,
35 | sources: `${defaultSettings.srcPath}/sources/`,
36 | stores: `${defaultSettings.srcPath}/stores/`,
37 | styles: `${defaultSettings.srcPath}/styles/`,
38 | config: `${defaultSettings.srcPath}/config/` + process.env.REACT_WEBPACK_ENV
39 | }
40 | },
41 | module: {}
42 | };
43 |
--------------------------------------------------------------------------------
/cfg/defaults.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const path = require('path');
3 | const srcPath = path.join(__dirname, '/../src');
4 | const dfltPort = 8000;
5 | function getDefaultModules() {
6 | return {
7 | // preLoaders: [{
8 | // test: /\.(js|jsx)$/,
9 | // include: srcPath,
10 | // loader: 'eslint-loader'
11 | // }],
12 | loaders: [
13 | {
14 | test: /\.css$/,
15 | loader: 'style-loader!css-loader!postcss-loader'
16 | },
17 | {
18 | test: /\.sass/,
19 | loader: 'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded&indentedSyntax'
20 | },
21 | {
22 | test: /\.scss/,
23 | loader: 'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded'
24 | },
25 | {
26 | test: /\.less/,
27 | loader: 'style-loader!css-loader!postcss-loader!less-loader'
28 | },
29 | {
30 | test: /\.styl/,
31 | loader: 'style-loader!css-loader!postcss-loader!stylus-loader'
32 | },
33 | {
34 | test: /\.(png|jpg|gif|woff|woff2)$/,
35 | loader: 'url-loader?limit=8192'
36 | },
37 | {
38 | test: /\.(mp4|ogg|svg)$/,
39 | loader: 'file-loader'
40 | }
41 | ]
42 | };
43 | }
44 | module.exports = {
45 | srcPath: srcPath,
46 | publicPath: '/assets/',
47 | port: dfltPort,
48 | getDefaultModules: getDefaultModules,
49 | postcss: function () {
50 | return [];
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/cfg/dev.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let webpack = require('webpack');
5 | let baseConfig = require('./base');
6 | let defaultSettings = require('./defaults');
7 |
8 | // Add needed plugins here
9 | let BowerWebpackPlugin = require('bower-webpack-plugin');
10 |
11 | let config = Object.assign({}, baseConfig, {
12 | entry: [
13 | 'webpack-dev-server/client?http://127.0.0.1:' + defaultSettings.port,
14 | 'webpack/hot/only-dev-server',
15 | './src/index'
16 | ],
17 | cache: true,
18 | devtool: 'eval',
19 | plugins: [
20 | new webpack.HotModuleReplacementPlugin(),
21 | new webpack.NoErrorsPlugin(),
22 | new BowerWebpackPlugin({
23 | searchResolveModulesDirectories: false
24 | })
25 | ],
26 | module: defaultSettings.getDefaultModules()
27 | });
28 |
29 | // Add needed loaders to the defaults here
30 | config.module.loaders.push({
31 | test: /\.(js|jsx)$/,
32 | loader: 'react-hot!babel-loader',
33 | include: [].concat(
34 | config.additionalPaths,
35 | [ path.join(__dirname, '/../src') ]
36 | )
37 | });
38 |
39 | module.exports = config;
40 |
--------------------------------------------------------------------------------
/cfg/dist.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let webpack = require('webpack');
5 |
6 | let baseConfig = require('./base');
7 | let defaultSettings = require('./defaults');
8 |
9 | // Add needed plugins here
10 | let BowerWebpackPlugin = require('bower-webpack-plugin');
11 |
12 | let config = Object.assign({}, baseConfig, {
13 | entry: path.join(__dirname, '../src/index'),
14 | cache: false,
15 | devtool: 'sourcemap',
16 | plugins: [
17 | new webpack.optimize.DedupePlugin(),
18 | new webpack.DefinePlugin({
19 | 'process.env.NODE_ENV': '"production"'
20 | }),
21 | new BowerWebpackPlugin({
22 | searchResolveModulesDirectories: false
23 | }),
24 | new webpack.optimize.UglifyJsPlugin(),
25 | new webpack.optimize.OccurenceOrderPlugin(),
26 | new webpack.optimize.AggressiveMergingPlugin(),
27 | new webpack.NoErrorsPlugin()
28 | ],
29 | module: defaultSettings.getDefaultModules()
30 | });
31 |
32 | // Add needed loaders to the defaults here
33 | config.module.loaders.push({
34 | test: /\.(js|jsx)$/,
35 | loader: 'babel',
36 | include: [].concat(
37 | config.additionalPaths,
38 | [ path.join(__dirname, '/../src') ]
39 | )
40 | });
41 |
42 | module.exports = config;
43 |
--------------------------------------------------------------------------------
/cfg/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let path = require('path');
4 | let srcPath = path.join(__dirname, '/../src/');
5 |
6 | let baseConfig = require('./base');
7 |
8 | // Add needed plugins here
9 | let BowerWebpackPlugin = require('bower-webpack-plugin');
10 |
11 | module.exports = {
12 | devtool: 'eval',
13 | module: {
14 | preLoaders: [
15 | {
16 | test: /\.(js|jsx)$/,
17 | loader: 'isparta-instrumenter-loader',
18 | include: [
19 | path.join(__dirname, '/../src')
20 | ]
21 | }
22 | ],
23 | loaders: [
24 | {
25 | test: /\.(png|jpg|gif|woff|woff2|css|sass|scss|less|styl)$/,
26 | loader: 'null-loader'
27 | },
28 | {
29 | test: /\.(js|jsx)$/,
30 | loader: 'babel-loader',
31 | include: [].concat(
32 | baseConfig.additionalPaths,
33 | [
34 | path.join(__dirname, '/../src'),
35 | path.join(__dirname, '/../test')
36 | ]
37 | )
38 | }
39 | ]
40 | },
41 | resolve: {
42 | extensions: [ '', '.js', '.jsx' ],
43 | alias: {
44 | actions: srcPath + 'actions/',
45 | helpers: path.join(__dirname, '/../test/helpers'),
46 | components: srcPath + 'components/',
47 | sources: srcPath + 'sources/',
48 | stores: srcPath + 'stores/',
49 | styles: srcPath + 'styles/',
50 | config: srcPath + 'config/' + process.env.REACT_WEBPACK_ENV
51 | }
52 | },
53 | plugins: [
54 | new BowerWebpackPlugin({
55 | searchResolveModulesDirectories: false
56 | })
57 | ]
58 | };
59 |
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
1 | # About the dist folder
2 | After building the dist version of your project, the generated files are stored in this folder. You should keep it under version control.
3 |
--------------------------------------------------------------------------------
/dist/assets/emoji.css:
--------------------------------------------------------------------------------
1 | .emoji { background: url('./emoji.png') no-repeat -25px -25px; width: 24px; height: 24px; color: transparent; display: inline-block; vertical-align: baseline; zoom: 1; }
2 | .emoji.s_-1 { background-position: 0px 0px; }
3 | .emoji.s_thumbsdown { background-position: -25px 0px; }
4 | .emoji.s_1 { background-position: -50px 0px; }
5 | .emoji.s_thumbsup { background-position: -75px 0px; }
6 | .emoji.s_8ball { background-position: -100px 0px; }
7 | .emoji.s_100 { background-position: -125px 0px; }
8 | .emoji.s_1234 { background-position: -150px 0px; }
9 | .emoji.s_a { background-position: -175px 0px; }
10 | .emoji.s_ab { background-position: -200px 0px; }
11 | .emoji.s_abc { background-position: -225px 0px; }
12 | .emoji.s_abcd { background-position: -250px 0px; }
13 | .emoji.s_accept { background-position: -275px 0px; }
14 | .emoji.s_aerial_tramway { background-position: -300px 0px; }
15 | .emoji.s_airplane { background-position: -325px 0px; }
16 | .emoji.s_alarm_clock { background-position: -350px 0px; }
17 | .emoji.s_alien { background-position: -375px 0px; }
18 | .emoji.s_ambulance { background-position: -400px 0px; }
19 | .emoji.s_anchor { background-position: -425px 0px; }
20 | .emoji.s_angel { background-position: -450px 0px; }
21 | .emoji.s_anger { background-position: -475px 0px; }
22 | .emoji.s_angry { background-position: -500px 0px; }
23 | .emoji.s_anguished { background-position: -525px 0px; }
24 | .emoji.s_ant { background-position: -550px 0px; }
25 | .emoji.s_apple { background-position: -575px 0px; }
26 | .emoji.s_aquarius { background-position: -600px 0px; }
27 | .emoji.s_aries { background-position: -625px 0px; }
28 | .emoji.s_arrow_backward { background-position: -650px 0px; }
29 | .emoji.s_arrow_double_down { background-position: -675px 0px; }
30 | .emoji.s_arrow_double_up { background-position: -700px 0px; }
31 | .emoji.s_arrow_down_small { background-position: -725px 0px; }
32 | .emoji.s_arrow_down { background-position: -750px 0px; }
33 | .emoji.s_arrow_forward { background-position: -775px 0px; }
34 | .emoji.s_arrow_heading_down { background-position: -800px 0px; }
35 | .emoji.s_arrow_heading_up { background-position: -825px 0px; }
36 | .emoji.s_arrow_left { background-position: -850px 0px; }
37 | .emoji.s_arrow_lower_left { background-position: -875px 0px; }
38 | .emoji.s_arrow_lower_right { background-position: -900px 0px; }
39 | .emoji.s_arrow_right_hook { background-position: -925px 0px; }
40 | .emoji.s_arrow_right { background-position: -950px 0px; }
41 | .emoji.s_arrow_up_down { background-position: -975px 0px; }
42 | .emoji.s_arrow_up_small { background-position: -1000px 0px; }
43 | .emoji.s_arrow_up { background-position: -1025px 0px; }
44 | .emoji.s_arrow_upper_left { background-position: -1050px 0px; }
45 | .emoji.s_arrow_upper_right { background-position: -1075px 0px; }
46 | .emoji.s_arrows_clockwise { background-position: -1100px 0px; }
47 | .emoji.s_arrows_counterclockwise { background-position: -1125px 0px; }
48 | .emoji.s_art { background-position: -1150px 0px; }
49 | .emoji.s_articulated_lorry { background-position: -1175px 0px; }
50 | .emoji.s_astonished { background-position: -1200px 0px; }
51 | .emoji.s_athletic_shoe { background-position: -1225px 0px; }
52 | .emoji.s_atm { background-position: -1250px 0px; }
53 | .emoji.s_b { background-position: -1275px 0px; }
54 | .emoji.s_baby_bottle { background-position: -1300px 0px; }
55 | .emoji.s_baby_chick { background-position: -1325px 0px; }
56 | .emoji.s_baby_symbol { background-position: -1350px 0px; }
57 | .emoji.s_baby { background-position: -1375px 0px; }
58 | .emoji.s_back { background-position: -1400px 0px; }
59 | .emoji.s_baggage_claim { background-position: -1425px 0px; }
60 | .emoji.s_balloon { background-position: -1450px 0px; }
61 | .emoji.s_ballot_box_with_check { background-position: -1475px 0px; }
62 | .emoji.s_bamboo { background-position: -1500px 0px; }
63 | .emoji.s_banana { background-position: -1525px 0px; }
64 | .emoji.s_bangbang { background-position: -1550px 0px; }
65 | .emoji.s_bank { background-position: -1575px 0px; }
66 | .emoji.s_bar_chart { background-position: -1600px 0px; }
67 | .emoji.s_barber { background-position: -1625px 0px; }
68 | .emoji.s_baseball { background-position: -1650px 0px; }
69 | .emoji.s_basketball { background-position: -1675px 0px; }
70 | .emoji.s_bath { background-position: -1700px 0px; }
71 | .emoji.s_bathtub { background-position: -1725px 0px; }
72 | .emoji.s_battery { background-position: -1750px 0px; }
73 | .emoji.s_bear { background-position: -1775px 0px; }
74 | .emoji.s_honeybee { background-position: -1800px 0px; }
75 | .emoji.s_bee { background-position: -1825px 0px; }
76 | .emoji.s_beer { background-position: -1850px 0px; }
77 | .emoji.s_beers { background-position: -1875px 0px; }
78 | .emoji.s_beetle { background-position: -1900px 0px; }
79 | .emoji.s_beginner { background-position: -1925px 0px; }
80 | .emoji.s_bell { background-position: -1950px 0px; }
81 | .emoji.s_bento { background-position: -1975px 0px; }
82 | .emoji.s_bicyclist { background-position: -2000px 0px; }
83 | .emoji.s_bike { background-position: -2025px 0px; }
84 | .emoji.s_bikini { background-position: -2050px 0px; }
85 | .emoji.s_bird { background-position: -2075px 0px; }
86 | .emoji.s_birthday { background-position: -2100px 0px; }
87 | .emoji.s_black_circle { background-position: -2125px 0px; }
88 | .emoji.s_black_joker { background-position: -2150px 0px; }
89 | .emoji.s_black_large_square { background-position: -2175px 0px; }
90 | .emoji.s_black_medium_small_square { background-position: -2200px 0px; }
91 | .emoji.s_black_medium_square { background-position: -2225px 0px; }
92 | .emoji.s_black_nib { background-position: -2250px 0px; }
93 | .emoji.s_black_small_square { background-position: -2275px 0px; }
94 | .emoji.s_black_square_button { background-position: -2300px 0px; }
95 | .emoji.s_blossom { background-position: -2325px 0px; }
96 | .emoji.s_blowfish { background-position: -2350px 0px; }
97 | .emoji.s_blue_book { background-position: -2375px 0px; }
98 | .emoji.s_blue_car { background-position: -2400px 0px; }
99 | .emoji.s_blue_heart { background-position: -2425px 0px; }
100 | .emoji.s_blush { background-position: -2450px 0px; }
101 | .emoji.s_boar { background-position: -2475px 0px; }
102 | .emoji.s_sailboat { background-position: -2500px 0px; }
103 | .emoji.s_boat { background-position: -2525px 0px; }
104 | .emoji.s_bomb { background-position: -2550px 0px; }
105 | .emoji.s_book { background-position: -2575px 0px; }
106 | .emoji.s_open_book { background-position: -2600px 0px; }
107 | .emoji.s_bookmark_tabs { background-position: -2625px 0px; }
108 | .emoji.s_bookmark { background-position: -2650px 0px; }
109 | .emoji.s_books { background-position: -2675px 0px; }
110 | .emoji.s_boom { background-position: -2700px 0px; }
111 | .emoji.s_collision { background-position: -2725px 0px; }
112 | .emoji.s_boot { background-position: -2750px 0px; }
113 | .emoji.s_bouquet { background-position: -2775px 0px; }
114 | .emoji.s_bow { background-position: -2800px 0px; }
115 | .emoji.s_bowling { background-position: -2825px 0px; }
116 | .emoji.s_bowtie { background-position: -2850px 0px; }
117 | .emoji.s_boy { background-position: -2875px 0px; }
118 | .emoji.s_bread { background-position: -2900px 0px; }
119 | .emoji.s_bride_with_veil { background-position: -2925px 0px; }
120 | .emoji.s_bridge_at_night { background-position: -2950px 0px; }
121 | .emoji.s_briefcase { background-position: -2975px 0px; }
122 | .emoji.s_broken_heart { background-position: -3000px 0px; }
123 | .emoji.s_bug { background-position: -3025px 0px; }
124 | .emoji.s_bulb { background-position: -3050px 0px; }
125 | .emoji.s_bullettrain_front { background-position: -3075px 0px; }
126 | .emoji.s_bullettrain_side { background-position: -3100px 0px; }
127 | .emoji.s_bus { background-position: -3125px 0px; }
128 | .emoji.s_busstop { background-position: -3150px 0px; }
129 | .emoji.s_bust_in_silhouette { background-position: -3175px 0px; }
130 | .emoji.s_busts_in_silhouette { background-position: -3200px 0px; }
131 | .emoji.s_cactus { background-position: -3225px 0px; }
132 | .emoji.s_cake { background-position: -3250px 0px; }
133 | .emoji.s_calendar { background-position: -3275px 0px; }
134 | .emoji.s_calling { background-position: -3300px 0px; }
135 | .emoji.s_camel { background-position: -3325px 0px; }
136 | .emoji.s_camera { background-position: -3350px 0px; }
137 | .emoji.s_cancer { background-position: -3375px 0px; }
138 | .emoji.s_candy { background-position: -3400px 0px; }
139 | .emoji.s_capital_abcd { background-position: -3425px 0px; }
140 | .emoji.s_capricorn { background-position: -3450px 0px; }
141 | .emoji.s_red_car { background-position: -3475px 0px; }
142 | .emoji.s_car { background-position: -3500px 0px; }
143 | .emoji.s_card_index { background-position: -3525px 0px; }
144 | .emoji.s_carousel_horse { background-position: -3550px 0px; }
145 | .emoji.s_cat { background-position: -3575px 0px; }
146 | .emoji.s_cat2 { background-position: -3600px 0px; }
147 | .emoji.s_cd { background-position: -3625px 0px; }
148 | .emoji.s_chart_with_downwards_trend { background-position: -3650px 0px; }
149 | .emoji.s_chart_with_upwards_trend { background-position: -3675px 0px; }
150 | .emoji.s_chart { background-position: -3700px 0px; }
151 | .emoji.s_checkered_flag { background-position: -3725px 0px; }
152 | .emoji.s_cherries { background-position: -3750px 0px; }
153 | .emoji.s_cherry_blossom { background-position: -3775px 0px; }
154 | .emoji.s_chestnut { background-position: -3800px 0px; }
155 | .emoji.s_chicken { background-position: -3825px 0px; }
156 | .emoji.s_children_crossing { background-position: -3850px 0px; }
157 | .emoji.s_chocolate_bar { background-position: -3875px 0px; }
158 | .emoji.s_christmas_tree { background-position: -3900px 0px; }
159 | .emoji.s_church { background-position: -3925px 0px; }
160 | .emoji.s_cinema { background-position: -3950px 0px; }
161 | .emoji.s_circus_tent { background-position: -3975px 0px; }
162 | .emoji.s_city_sunrise { background-position: -4000px 0px; }
163 | .emoji.s_city_sunset { background-position: -4025px 0px; }
164 | .emoji.s_cl { background-position: -4050px 0px; }
165 | .emoji.s_clap { background-position: -4075px 0px; }
166 | .emoji.s_clapper { background-position: -4100px 0px; }
167 | .emoji.s_clipboard { background-position: -4125px 0px; }
168 | .emoji.s_clock1 { background-position: -4150px 0px; }
169 | .emoji.s_clock2 { background-position: -4175px 0px; }
170 | .emoji.s_clock3 { background-position: -4200px 0px; }
171 | .emoji.s_clock4 { background-position: -4225px 0px; }
172 | .emoji.s_clock5 { background-position: -4250px 0px; }
173 | .emoji.s_clock6 { background-position: -4275px 0px; }
174 | .emoji.s_clock7 { background-position: -4300px 0px; }
175 | .emoji.s_clock8 { background-position: -4325px 0px; }
176 | .emoji.s_clock9 { background-position: -4350px 0px; }
177 | .emoji.s_clock10 { background-position: -4375px 0px; }
178 | .emoji.s_clock11 { background-position: -4400px 0px; }
179 | .emoji.s_clock12 { background-position: -4425px 0px; }
180 | .emoji.s_clock130 { background-position: -4450px 0px; }
181 | .emoji.s_clock230 { background-position: -4475px 0px; }
182 | .emoji.s_clock330 { background-position: -4500px 0px; }
183 | .emoji.s_clock430 { background-position: -4525px 0px; }
184 | .emoji.s_clock530 { background-position: -4550px 0px; }
185 | .emoji.s_clock630 { background-position: -4575px 0px; }
186 | .emoji.s_clock730 { background-position: -4600px 0px; }
187 | .emoji.s_clock830 { background-position: -4625px 0px; }
188 | .emoji.s_clock930 { background-position: -4650px 0px; }
189 | .emoji.s_clock1030 { background-position: -4675px 0px; }
190 | .emoji.s_clock1130 { background-position: -4700px 0px; }
191 | .emoji.s_clock1230 { background-position: -4725px 0px; }
192 | .emoji.s_closed_book { background-position: -4750px 0px; }
193 | .emoji.s_closed_lock_with_key { background-position: -4775px 0px; }
194 | .emoji.s_closed_umbrella { background-position: -4800px 0px; }
195 | .emoji.s_cloud { background-position: -4825px 0px; }
196 | .emoji.s_clubs { background-position: -4850px 0px; }
197 | .emoji.s_cn { background-position: -4875px 0px; }
198 | .emoji.s_cocktail { background-position: -4900px 0px; }
199 | .emoji.s_coffee { background-position: -4925px 0px; }
200 | .emoji.s_cold_sweat { background-position: -4950px 0px; }
201 | .emoji.s_computer { background-position: -4975px 0px; }
202 | .emoji.s_confetti_ball { background-position: -5000px 0px; }
203 | .emoji.s_confounded { background-position: -5025px 0px; }
204 | .emoji.s_confused { background-position: -5050px 0px; }
205 | .emoji.s_congratulations { background-position: -5075px 0px; }
206 | .emoji.s_construction_worker { background-position: -5100px 0px; }
207 | .emoji.s_construction { background-position: -5125px 0px; }
208 | .emoji.s_convenience_store { background-position: -5150px 0px; }
209 | .emoji.s_cookie { background-position: -5175px 0px; }
210 | .emoji.s_cool { background-position: -5200px 0px; }
211 | .emoji.s_cop { background-position: -5225px 0px; }
212 | .emoji.s_copyright { background-position: -5250px 0px; }
213 | .emoji.s_corn { background-position: -5275px 0px; }
214 | .emoji.s_couple_with_heart { background-position: -5300px 0px; }
215 | .emoji.s_couple { background-position: -5325px 0px; }
216 | .emoji.s_couplekiss { background-position: -5350px 0px; }
217 | .emoji.s_cow { background-position: -5375px 0px; }
218 | .emoji.s_cow2 { background-position: -5400px 0px; }
219 | .emoji.s_credit_card { background-position: -5425px 0px; }
220 | .emoji.s_crescent_moon { background-position: -5450px 0px; }
221 | .emoji.s_crocodile { background-position: -5475px 0px; }
222 | .emoji.s_crossed_flags { background-position: -5500px 0px; }
223 | .emoji.s_crown { background-position: -5525px 0px; }
224 | .emoji.s_cry { background-position: -5550px 0px; }
225 | .emoji.s_crying_cat_face { background-position: -5575px 0px; }
226 | .emoji.s_crystal_ball { background-position: -5600px 0px; }
227 | .emoji.s_cupid { background-position: -5625px 0px; }
228 | .emoji.s_curly_loop { background-position: -5650px 0px; }
229 | .emoji.s_currency_exchange { background-position: -5675px 0px; }
230 | .emoji.s_curry { background-position: -5700px 0px; }
231 | .emoji.s_custard { background-position: -5725px 0px; }
232 | .emoji.s_customs { background-position: -5750px 0px; }
233 | .emoji.s_cyclone { background-position: -5775px 0px; }
234 | .emoji.s_dancer { background-position: -5800px 0px; }
235 | .emoji.s_dancers { background-position: -5825px 0px; }
236 | .emoji.s_dango { background-position: -5850px 0px; }
237 | .emoji.s_dart { background-position: -5875px 0px; }
238 | .emoji.s_dash { background-position: -5900px 0px; }
239 | .emoji.s_date { background-position: -5925px 0px; }
240 | .emoji.s_de { background-position: -5950px 0px; }
241 | .emoji.s_deciduous_tree { background-position: -5975px 0px; }
242 | .emoji.s_department_store { background-position: -6000px 0px; }
243 | .emoji.s_diamond_shape_with_a_dot_inside { background-position: -6025px 0px; }
244 | .emoji.s_diamonds { background-position: -6050px 0px; }
245 | .emoji.s_disappointed_relieved { background-position: -6075px 0px; }
246 | .emoji.s_disappointed { background-position: -6100px 0px; }
247 | .emoji.s_dizzy_face { background-position: -6125px 0px; }
248 | .emoji.s_dizzy { background-position: -6150px 0px; }
249 | .emoji.s_do_not_litter { background-position: -6175px 0px; }
250 | .emoji.s_dog { background-position: -6200px 0px; }
251 | .emoji.s_dog2 { background-position: -6225px 0px; }
252 | .emoji.s_dollar { background-position: -6250px 0px; }
253 | .emoji.s_dolls { background-position: -6275px 0px; }
254 | .emoji.s_dolphin { background-position: -6300px 0px; }
255 | .emoji.s_door { background-position: -6325px 0px; }
256 | .emoji.s_doughnut { background-position: -6350px 0px; }
257 | .emoji.s_dragon_face { background-position: -6375px 0px; }
258 | .emoji.s_dragon { background-position: -6400px 0px; }
259 | .emoji.s_dress { background-position: -6425px 0px; }
260 | .emoji.s_dromedary_camel { background-position: -6450px 0px; }
261 | .emoji.s_droplet { background-position: -6475px 0px; }
262 | .emoji.s_dvd { background-position: -6500px 0px; }
263 | .emoji.s_e-mail { background-position: -6525px 0px; }
264 | .emoji.s_ear_of_rice { background-position: -6550px 0px; }
265 | .emoji.s_ear { background-position: -6575px 0px; }
266 | .emoji.s_earth_africa { background-position: -6600px 0px; }
267 | .emoji.s_earth_americas { background-position: -6625px 0px; }
268 | .emoji.s_earth_asia { background-position: -6650px 0px; }
269 | .emoji.s_egg { background-position: -6675px 0px; }
270 | .emoji.s_eggplant { background-position: -6700px 0px; }
271 | .emoji.s_eight_pointed_black_star { background-position: -6725px 0px; }
272 | .emoji.s_eight_spoked_asterisk { background-position: -6750px 0px; }
273 | .emoji.s_eight { background-position: -6775px 0px; }
274 | .emoji.s_electric_plug { background-position: -6800px 0px; }
275 | .emoji.s_elephant { background-position: -6825px 0px; }
276 | .emoji.s_envelope { background-position: -6850px 0px; }
277 | .emoji.s_email { background-position: -6875px 0px; }
278 | .emoji.s_end { background-position: -6900px 0px; }
279 | .emoji.s_envelope_with_arrow { background-position: -6925px 0px; }
280 | .emoji.s_es { background-position: -6950px 0px; }
281 | .emoji.s_euro { background-position: -6975px 0px; }
282 | .emoji.s_european_castle { background-position: -7000px 0px; }
283 | .emoji.s_european_post_office { background-position: -7025px 0px; }
284 | .emoji.s_evergreen_tree { background-position: -7050px 0px; }
285 | .emoji.s_exclamation { background-position: -7075px 0px; }
286 | .emoji.s_heavy_exclamation_mark { background-position: -7100px 0px; }
287 | .emoji.s_expressionless { background-position: -7125px 0px; }
288 | .emoji.s_eyeglasses { background-position: -7150px 0px; }
289 | .emoji.s_eyes { background-position: -7175px 0px; }
290 | .emoji.s_punch { background-position: -7200px 0px; }
291 | .emoji.s_facepunch { background-position: -7225px 0px; }
292 | .emoji.s_factory { background-position: -7250px 0px; }
293 | .emoji.s_fallen_leaf { background-position: -7275px 0px; }
294 | .emoji.s_family { background-position: -7300px 0px; }
295 | .emoji.s_fast_forward { background-position: -7325px 0px; }
296 | .emoji.s_fax { background-position: -7350px 0px; }
297 | .emoji.s_fearful { background-position: -7375px 0px; }
298 | .emoji.s_feelsgood { background-position: -7400px 0px; }
299 | .emoji.s_paw_prints { background-position: -7425px 0px; }
300 | .emoji.s_feet { background-position: -7450px 0px; }
301 | .emoji.s_ferris_wheel { background-position: -7475px 0px; }
302 | .emoji.s_file_folder { background-position: -7500px 0px; }
303 | .emoji.s_finnadie { background-position: -7525px 0px; }
304 | .emoji.s_fire_engine { background-position: -7550px 0px; }
305 | .emoji.s_fire { background-position: -7575px 0px; }
306 | .emoji.s_fireworks { background-position: -7600px 0px; }
307 | .emoji.s_first_quarter_moon_with_face { background-position: -7625px 0px; }
308 | .emoji.s_first_quarter_moon { background-position: -7650px 0px; }
309 | .emoji.s_fish_cake { background-position: -7675px 0px; }
310 | .emoji.s_fish { background-position: -7700px 0px; }
311 | .emoji.s_fishing_pole_and_fish { background-position: -7725px 0px; }
312 | .emoji.s_fist { background-position: -7750px 0px; }
313 | .emoji.s_five { background-position: -7775px 0px; }
314 | .emoji.s_flags { background-position: -7800px 0px; }
315 | .emoji.s_flashlight { background-position: -7825px 0px; }
316 | .emoji.s_floppy_disk { background-position: -7850px 0px; }
317 | .emoji.s_flower_playing_cards { background-position: -7875px 0px; }
318 | .emoji.s_flushed { background-position: -7900px 0px; }
319 | .emoji.s_foggy { background-position: -7925px 0px; }
320 | .emoji.s_football { background-position: -7950px 0px; }
321 | .emoji.s_footprints { background-position: -7975px 0px; }
322 | .emoji.s_fork_and_knife { background-position: -8000px 0px; }
323 | .emoji.s_fountain { background-position: -8025px 0px; }
324 | .emoji.s_four_leaf_clover { background-position: -8050px 0px; }
325 | .emoji.s_four { background-position: -8075px 0px; }
326 | .emoji.s_fr { background-position: -8100px 0px; }
327 | .emoji.s_free { background-position: -8125px 0px; }
328 | .emoji.s_fried_shrimp { background-position: -8150px 0px; }
329 | .emoji.s_fries { background-position: -8175px 0px; }
330 | .emoji.s_frog { background-position: -8200px 0px; }
331 | .emoji.s_frowning { background-position: -8225px 0px; }
332 | .emoji.s_fuelpump { background-position: -8250px 0px; }
333 | .emoji.s_full_moon_with_face { background-position: -8275px 0px; }
334 | .emoji.s_full_moon { background-position: -8300px 0px; }
335 | .emoji.s_game_die { background-position: -8325px 0px; }
336 | .emoji.s_gb { background-position: -8350px 0px; }
337 | .emoji.s_uk { background-position: -8375px 0px; }
338 | .emoji.s_gem { background-position: -8400px 0px; }
339 | .emoji.s_gemini { background-position: -8425px 0px; }
340 | .emoji.s_ghost { background-position: -8450px 0px; }
341 | .emoji.s_gift_heart { background-position: -8475px 0px; }
342 | .emoji.s_gift { background-position: -8500px 0px; }
343 | .emoji.s_girl { background-position: -8525px 0px; }
344 | .emoji.s_globe_with_meridians { background-position: -8550px 0px; }
345 | .emoji.s_goat { background-position: -8575px 0px; }
346 | .emoji.s_goberserk { background-position: -8600px 0px; }
347 | .emoji.s_godmode { background-position: -8625px 0px; }
348 | .emoji.s_golf { background-position: -8650px 0px; }
349 | .emoji.s_grapes { background-position: -8675px 0px; }
350 | .emoji.s_green_apple { background-position: -8700px 0px; }
351 | .emoji.s_green_book { background-position: -8725px 0px; }
352 | .emoji.s_green_heart { background-position: -8750px 0px; }
353 | .emoji.s_grey_exclamation { background-position: -8775px 0px; }
354 | .emoji.s_grey_question { background-position: -8800px 0px; }
355 | .emoji.s_grimacing { background-position: -8825px 0px; }
356 | .emoji.s_grin { background-position: -8850px 0px; }
357 | .emoji.s_grinning { background-position: -8875px 0px; }
358 | .emoji.s_guardsman { background-position: -8900px 0px; }
359 | .emoji.s_guitar { background-position: -8925px 0px; }
360 | .emoji.s_gun { background-position: -8950px 0px; }
361 | .emoji.s_haircut { background-position: -8975px 0px; }
362 | .emoji.s_hamburger { background-position: -9000px 0px; }
363 | .emoji.s_hammer { background-position: -9025px 0px; }
364 | .emoji.s_hamster { background-position: -9050px 0px; }
365 | .emoji.s_hand { background-position: -9075px 0px; }
366 | .emoji.s_raised_hand { background-position: -9100px 0px; }
367 | .emoji.s_handbag { background-position: -9125px 0px; }
368 | .emoji.s_poop { background-position: -9150px 0px; }
369 | .emoji.s_hankey { background-position: -9175px 0px; }
370 | .emoji.s_shit { background-position: -9200px 0px; }
371 | .emoji.s_hash { background-position: -9225px 0px; }
372 | .emoji.s_hatched_chick { background-position: -9250px 0px; }
373 | .emoji.s_hatching_chick { background-position: -9275px 0px; }
374 | .emoji.s_headphones { background-position: -9300px 0px; }
375 | .emoji.s_hear_no_evil { background-position: -9325px 0px; }
376 | .emoji.s_heart_decoration { background-position: -9350px 0px; }
377 | .emoji.s_heart_eyes_cat { background-position: -9375px 0px; }
378 | .emoji.s_heart_eyes { background-position: -9400px 0px; }
379 | .emoji.s_heart { background-position: -9425px 0px; }
380 | .emoji.s_heartbeat { background-position: -9450px 0px; }
381 | .emoji.s_heartpulse { background-position: -9475px 0px; }
382 | .emoji.s_hearts { background-position: -9500px 0px; }
383 | .emoji.s_heavy_check_mark { background-position: -9525px 0px; }
384 | .emoji.s_heavy_division_sign { background-position: -9550px 0px; }
385 | .emoji.s_heavy_dollar_sign { background-position: -9575px 0px; }
386 | .emoji.s_heavy_minus_sign { background-position: -9600px 0px; }
387 | .emoji.s_heavy_multiplication_x { background-position: -9625px 0px; }
388 | .emoji.s_heavy_plus_sign { background-position: -9650px 0px; }
389 | .emoji.s_helicopter { background-position: -9675px 0px; }
390 | .emoji.s_herb { background-position: -9700px 0px; }
391 | .emoji.s_hibiscus { background-position: -9725px 0px; }
392 | .emoji.s_high_brightness { background-position: -9750px 0px; }
393 | .emoji.s_high_heel { background-position: -9775px 0px; }
394 | .emoji.s_hocho { background-position: -9800px 0px; }
395 | .emoji.s_honey_pot { background-position: -9825px 0px; }
396 | .emoji.s_horse_racing { background-position: -9850px 0px; }
397 | .emoji.s_horse { background-position: -9875px 0px; }
398 | .emoji.s_hospital { background-position: -9900px 0px; }
399 | .emoji.s_hotel { background-position: -9925px 0px; }
400 | .emoji.s_hotsprings { background-position: -9950px 0px; }
401 | .emoji.s_hourglass_flowing_sand { background-position: -9975px 0px; }
402 | .emoji.s_hourglass { background-position: -10000px 0px; }
403 | .emoji.s_house_with_garden { background-position: -10025px 0px; }
404 | .emoji.s_house { background-position: -10050px 0px; }
405 | .emoji.s_hurtrealbad { background-position: -10075px 0px; }
406 | .emoji.s_hushed { background-position: -10100px 0px; }
407 | .emoji.s_ice_cream { background-position: -10125px 0px; }
408 | .emoji.s_icecream { background-position: -10150px 0px; }
409 | .emoji.s_id { background-position: -10175px 0px; }
410 | .emoji.s_ideograph_advantage { background-position: -10200px 0px; }
411 | .emoji.s_imp { background-position: -10225px 0px; }
412 | .emoji.s_inbox_tray { background-position: -10250px 0px; }
413 | .emoji.s_incoming_envelope { background-position: -10275px 0px; }
414 | .emoji.s_information_desk_person { background-position: -10300px 0px; }
415 | .emoji.s_information_source { background-position: -10325px 0px; }
416 | .emoji.s_innocent { background-position: -10350px 0px; }
417 | .emoji.s_interrobang { background-position: -10375px 0px; }
418 | .emoji.s_iphone { background-position: -10400px 0px; }
419 | .emoji.s_it { background-position: -10425px 0px; }
420 | .emoji.s_lantern { background-position: -10450px 0px; }
421 | .emoji.s_izakaya_lantern { background-position: -10475px 0px; }
422 | .emoji.s_jack_o_lantern { background-position: -10500px 0px; }
423 | .emoji.s_japan { background-position: -10525px 0px; }
424 | .emoji.s_japanese_castle { background-position: -10550px 0px; }
425 | .emoji.s_japanese_goblin { background-position: -10575px 0px; }
426 | .emoji.s_japanese_ogre { background-position: -10600px 0px; }
427 | .emoji.s_jeans { background-position: -10625px 0px; }
428 | .emoji.s_joy_cat { background-position: -10650px 0px; }
429 | .emoji.s_joy { background-position: -10675px 0px; }
430 | .emoji.s_jp { background-position: -10700px 0px; }
431 | .emoji.s_key { background-position: -10725px 0px; }
432 | .emoji.s_keycap_ten { background-position: -10750px 0px; }
433 | .emoji.s_kimono { background-position: -10775px 0px; }
434 | .emoji.s_kiss { background-position: -10800px 0px; }
435 | .emoji.s_kissing_cat { background-position: -10825px 0px; }
436 | .emoji.s_kissing_closed_eyes { background-position: -10850px 0px; }
437 | .emoji.s_kissing_heart { background-position: -10875px 0px; }
438 | .emoji.s_kissing_smiling_eyes { background-position: -10900px 0px; }
439 | .emoji.s_kissing { background-position: -10925px 0px; }
440 | .emoji.s_koala { background-position: -10950px 0px; }
441 | .emoji.s_koko { background-position: -10975px 0px; }
442 | .emoji.s_kr { background-position: -11000px 0px; }
443 | .emoji.s_large_blue_circle { background-position: -11025px 0px; }
444 | .emoji.s_large_blue_diamond { background-position: -11050px 0px; }
445 | .emoji.s_large_orange_diamond { background-position: -11075px 0px; }
446 | .emoji.s_last_quarter_moon_with_face { background-position: -11100px 0px; }
447 | .emoji.s_last_quarter_moon { background-position: -11125px 0px; }
448 | .emoji.s_satisfied { background-position: -11150px 0px; }
449 | .emoji.s_laughing { background-position: -11175px 0px; }
450 | .emoji.s_leaves { background-position: -11200px 0px; }
451 | .emoji.s_ledger { background-position: -11225px 0px; }
452 | .emoji.s_left_luggage { background-position: -11250px 0px; }
453 | .emoji.s_left_right_arrow { background-position: -11275px 0px; }
454 | .emoji.s_leftwards_arrow_with_hook { background-position: -11300px 0px; }
455 | .emoji.s_lemon { background-position: -11325px 0px; }
456 | .emoji.s_leo { background-position: -11350px 0px; }
457 | .emoji.s_leopard { background-position: -11375px 0px; }
458 | .emoji.s_libra { background-position: -11400px 0px; }
459 | .emoji.s_light_rail { background-position: -11425px 0px; }
460 | .emoji.s_link { background-position: -11450px 0px; }
461 | .emoji.s_lips { background-position: -11475px 0px; }
462 | .emoji.s_lipstick { background-position: -11500px 0px; }
463 | .emoji.s_lock_with_ink_pen { background-position: -11525px 0px; }
464 | .emoji.s_lock { background-position: -11550px 0px; }
465 | .emoji.s_lollipop { background-position: -11575px 0px; }
466 | .emoji.s_loop { background-position: -11600px 0px; }
467 | .emoji.s_loudspeaker { background-position: -11625px 0px; }
468 | .emoji.s_love_hotel { background-position: -11650px 0px; }
469 | .emoji.s_love_letter { background-position: -11675px 0px; }
470 | .emoji.s_low_brightness { background-position: -11700px 0px; }
471 | .emoji.s_m { background-position: -11725px 0px; }
472 | .emoji.s_mag_right { background-position: -11750px 0px; }
473 | .emoji.s_mag { background-position: -11775px 0px; }
474 | .emoji.s_mahjong { background-position: -11800px 0px; }
475 | .emoji.s_mailbox_closed { background-position: -11825px 0px; }
476 | .emoji.s_mailbox_with_mail { background-position: -11850px 0px; }
477 | .emoji.s_mailbox_with_no_mail { background-position: -11875px 0px; }
478 | .emoji.s_mailbox { background-position: -11900px 0px; }
479 | .emoji.s_man_with_gua_pi_mao { background-position: -11925px 0px; }
480 | .emoji.s_man_with_turban { background-position: -11950px 0px; }
481 | .emoji.s_man { background-position: -11975px 0px; }
482 | .emoji.s_mans_shoe { background-position: -12000px 0px; }
483 | .emoji.s_shoe { background-position: -12025px 0px; }
484 | .emoji.s_maple_leaf { background-position: -12050px 0px; }
485 | .emoji.s_mask { background-position: -12075px 0px; }
486 | .emoji.s_massage { background-position: -12100px 0px; }
487 | .emoji.s_meat_on_bone { background-position: -12125px 0px; }
488 | .emoji.s_mega { background-position: -12150px 0px; }
489 | .emoji.s_melon { background-position: -12175px 0px; }
490 | .emoji.s_memo { background-position: -12200px 0px; }
491 | .emoji.s_pencil { background-position: -12225px 0px; }
492 | .emoji.s_mens { background-position: -12250px 0px; }
493 | .emoji.s_metal { background-position: -12275px 0px; }
494 | .emoji.s_metro { background-position: -12300px 0px; }
495 | .emoji.s_microphone { background-position: -12325px 0px; }
496 | .emoji.s_microscope { background-position: -12350px 0px; }
497 | .emoji.s_milky_way { background-position: -12375px 0px; }
498 | .emoji.s_minibus { background-position: -12400px 0px; }
499 | .emoji.s_minidisc { background-position: -12425px 0px; }
500 | .emoji.s_mobile_phone_off { background-position: -12450px 0px; }
501 | .emoji.s_money_with_wings { background-position: -12475px 0px; }
502 | .emoji.s_moneybag { background-position: -12500px 0px; }
503 | .emoji.s_monkey_face { background-position: -12525px 0px; }
504 | .emoji.s_monkey { background-position: -12550px 0px; }
505 | .emoji.s_monorail { background-position: -12575px 0px; }
506 | .emoji.s_moon { background-position: -12600px 0px; }
507 | .emoji.s_waxing_gibbous_moon { background-position: -12625px 0px; }
508 | .emoji.s_mortar_board { background-position: -12650px 0px; }
509 | .emoji.s_mount_fuji { background-position: -12675px 0px; }
510 | .emoji.s_mountain_bicyclist { background-position: -12700px 0px; }
511 | .emoji.s_mountain_cableway { background-position: -12725px 0px; }
512 | .emoji.s_mountain_railway { background-position: -12750px 0px; }
513 | .emoji.s_mouse { background-position: -12775px 0px; }
514 | .emoji.s_mouse2 { background-position: -12800px 0px; }
515 | .emoji.s_movie_camera { background-position: -12825px 0px; }
516 | .emoji.s_moyai { background-position: -12850px 0px; }
517 | .emoji.s_muscle { background-position: -12875px 0px; }
518 | .emoji.s_mushroom { background-position: -12900px 0px; }
519 | .emoji.s_musical_keyboard { background-position: -12925px 0px; }
520 | .emoji.s_musical_note { background-position: -12950px 0px; }
521 | .emoji.s_musical_score { background-position: -12975px 0px; }
522 | .emoji.s_mute { background-position: -13000px 0px; }
523 | .emoji.s_nail_care { background-position: -13025px 0px; }
524 | .emoji.s_name_badge { background-position: -13050px 0px; }
525 | .emoji.s_neckbeard { background-position: -13075px 0px; }
526 | .emoji.s_necktie { background-position: -13100px 0px; }
527 | .emoji.s_negative_squared_cross_mark { background-position: -13125px 0px; }
528 | .emoji.s_neutral_face { background-position: -13150px 0px; }
529 | .emoji.s_new_moon_with_face { background-position: -13175px 0px; }
530 | .emoji.s_new_moon { background-position: -13200px 0px; }
531 | .emoji.s_new { background-position: -13225px 0px; }
532 | .emoji.s_newspaper { background-position: -13250px 0px; }
533 | .emoji.s_ng { background-position: -13275px 0px; }
534 | .emoji.s_nine { background-position: -13300px 0px; }
535 | .emoji.s_no_bell { background-position: -13325px 0px; }
536 | .emoji.s_no_bicycles { background-position: -13350px 0px; }
537 | .emoji.s_no_entry_sign { background-position: -13375px 0px; }
538 | .emoji.s_no_entry { background-position: -13400px 0px; }
539 | .emoji.s_no_good { background-position: -13425px 0px; }
540 | .emoji.s_no_mobile_phones { background-position: -13450px 0px; }
541 | .emoji.s_no_mouth { background-position: -13475px 0px; }
542 | .emoji.s_no_pedestrians { background-position: -13500px 0px; }
543 | .emoji.s_no_smoking { background-position: -13525px 0px; }
544 | .emoji.s_non-potable_water { background-position: -13550px 0px; }
545 | .emoji.s_nose { background-position: -13575px 0px; }
546 | .emoji.s_notebook_with_decorative_cover { background-position: -13600px 0px; }
547 | .emoji.s_notebook { background-position: -13625px 0px; }
548 | .emoji.s_notes { background-position: -13650px 0px; }
549 | .emoji.s_nut_and_bolt { background-position: -13675px 0px; }
550 | .emoji.s_o { background-position: -13700px 0px; }
551 | .emoji.s_o2 { background-position: -13725px 0px; }
552 | .emoji.s_ocean { background-position: -13750px 0px; }
553 | .emoji.s_octocat { background-position: -13775px 0px; }
554 | .emoji.s_octopus { background-position: -13800px 0px; }
555 | .emoji.s_oden { background-position: -13825px 0px; }
556 | .emoji.s_office { background-position: -13850px 0px; }
557 | .emoji.s_ok_hand { background-position: -13875px 0px; }
558 | .emoji.s_ok_woman { background-position: -13900px 0px; }
559 | .emoji.s_ok { background-position: -13925px 0px; }
560 | .emoji.s_older_man { background-position: -13950px 0px; }
561 | .emoji.s_older_woman { background-position: -13975px 0px; }
562 | .emoji.s_on { background-position: -14000px 0px; }
563 | .emoji.s_oncoming_automobile { background-position: -14025px 0px; }
564 | .emoji.s_oncoming_bus { background-position: -14050px 0px; }
565 | .emoji.s_oncoming_police_car { background-position: -14075px 0px; }
566 | .emoji.s_oncoming_taxi { background-position: -14100px 0px; }
567 | .emoji.s_one { background-position: -14125px 0px; }
568 | .emoji.s_open_file_folder { background-position: -14150px 0px; }
569 | .emoji.s_open_hands { background-position: -14175px 0px; }
570 | .emoji.s_open_mouth { background-position: -14200px 0px; }
571 | .emoji.s_ophiuchus { background-position: -14225px 0px; }
572 | .emoji.s_orange_book { background-position: -14250px 0px; }
573 | .emoji.s_outbox_tray { background-position: -14275px 0px; }
574 | .emoji.s_ox { background-position: -14300px 0px; }
575 | .emoji.s_package { background-position: -14325px 0px; }
576 | .emoji.s_page_facing_up { background-position: -14350px 0px; }
577 | .emoji.s_page_with_curl { background-position: -14375px 0px; }
578 | .emoji.s_pager { background-position: -14400px 0px; }
579 | .emoji.s_palm_tree { background-position: -14425px 0px; }
580 | .emoji.s_panda_face { background-position: -14450px 0px; }
581 | .emoji.s_paperclip { background-position: -14475px 0px; }
582 | .emoji.s_parking { background-position: -14500px 0px; }
583 | .emoji.s_part_alternation_mark { background-position: -14525px 0px; }
584 | .emoji.s_partly_sunny { background-position: -14550px 0px; }
585 | .emoji.s_passport_control { background-position: -14575px 0px; }
586 | .emoji.s_peach { background-position: -14600px 0px; }
587 | .emoji.s_pear { background-position: -14625px 0px; }
588 | .emoji.s_pencil2 { background-position: -14650px 0px; }
589 | .emoji.s_penguin { background-position: -14675px 0px; }
590 | .emoji.s_pensive { background-position: -14700px 0px; }
591 | .emoji.s_performing_arts { background-position: -14725px 0px; }
592 | .emoji.s_persevere { background-position: -14750px 0px; }
593 | .emoji.s_person_frowning { background-position: -14775px 0px; }
594 | .emoji.s_person_with_blond_hair { background-position: -14800px 0px; }
595 | .emoji.s_person_with_pouting_face { background-position: -14825px 0px; }
596 | .emoji.s_telephone { background-position: -14850px 0px; }
597 | .emoji.s_phone { background-position: -14875px 0px; }
598 | .emoji.s_pig_nose { background-position: -14900px 0px; }
599 | .emoji.s_pig { background-position: -14925px 0px; }
600 | .emoji.s_pig2 { background-position: -14950px 0px; }
601 | .emoji.s_pill { background-position: -14975px 0px; }
602 | .emoji.s_pineapple { background-position: -15000px 0px; }
603 | .emoji.s_pisces { background-position: -15025px 0px; }
604 | .emoji.s_pizza { background-position: -15050px 0px; }
605 | .emoji.s_point_down { background-position: -15075px 0px; }
606 | .emoji.s_point_left { background-position: -15100px 0px; }
607 | .emoji.s_point_right { background-position: -15125px 0px; }
608 | .emoji.s_point_up_2 { background-position: -15150px 0px; }
609 | .emoji.s_point_up { background-position: -15175px 0px; }
610 | .emoji.s_police_car { background-position: -15200px 0px; }
611 | .emoji.s_poodle { background-position: -15225px 0px; }
612 | .emoji.s_post_office { background-position: -15250px 0px; }
613 | .emoji.s_postal_horn { background-position: -15275px 0px; }
614 | .emoji.s_postbox { background-position: -15300px 0px; }
615 | .emoji.s_potable_water { background-position: -15325px 0px; }
616 | .emoji.s_pouch { background-position: -15350px 0px; }
617 | .emoji.s_poultry_leg { background-position: -15375px 0px; }
618 | .emoji.s_pound { background-position: -15400px 0px; }
619 | .emoji.s_pouting_cat { background-position: -15425px 0px; }
620 | .emoji.s_pray { background-position: -15450px 0px; }
621 | .emoji.s_princess { background-position: -15475px 0px; }
622 | .emoji.s_purple_heart { background-position: -15500px 0px; }
623 | .emoji.s_purse { background-position: -15525px 0px; }
624 | .emoji.s_pushpin { background-position: -15550px 0px; }
625 | .emoji.s_put_litter_in_its_place { background-position: -15575px 0px; }
626 | .emoji.s_question { background-position: -15600px 0px; }
627 | .emoji.s_rabbit { background-position: -15625px 0px; }
628 | .emoji.s_rabbit2 { background-position: -15650px 0px; }
629 | .emoji.s_racehorse { background-position: -15675px 0px; }
630 | .emoji.s_radio_button { background-position: -15700px 0px; }
631 | .emoji.s_radio { background-position: -15725px 0px; }
632 | .emoji.s_rage { background-position: -15750px 0px; }
633 | .emoji.s_rage1 { background-position: -15775px 0px; }
634 | .emoji.s_rage2 { background-position: -15800px 0px; }
635 | .emoji.s_rage3 { background-position: -15825px 0px; }
636 | .emoji.s_rage4 { background-position: -15850px 0px; }
637 | .emoji.s_railway_car { background-position: -15875px 0px; }
638 | .emoji.s_train { background-position: -15900px 0px; }
639 | .emoji.s_rainbow { background-position: -15925px 0px; }
640 | .emoji.s_raised_hands { background-position: -15950px 0px; }
641 | .emoji.s_raising_hand { background-position: -15975px 0px; }
642 | .emoji.s_ram { background-position: -16000px 0px; }
643 | .emoji.s_ramen { background-position: -16025px 0px; }
644 | .emoji.s_rat { background-position: -16050px 0px; }
645 | .emoji.s_recycle { background-position: -16075px 0px; }
646 | .emoji.s_red_circle { background-position: -16100px 0px; }
647 | .emoji.s_registered { background-position: -16125px 0px; }
648 | .emoji.s_relaxed { background-position: -16150px 0px; }
649 | .emoji.s_relieved { background-position: -16175px 0px; }
650 | .emoji.s_repeat_one { background-position: -16200px 0px; }
651 | .emoji.s_repeat { background-position: -16225px 0px; }
652 | .emoji.s_restroom { background-position: -16250px 0px; }
653 | .emoji.s_revolving_hearts { background-position: -16275px 0px; }
654 | .emoji.s_rewind { background-position: -16300px 0px; }
655 | .emoji.s_ribbon { background-position: -16325px 0px; }
656 | .emoji.s_rice_ball { background-position: -16350px 0px; }
657 | .emoji.s_rice_cracker { background-position: -16375px 0px; }
658 | .emoji.s_rice_scene { background-position: -16400px 0px; }
659 | .emoji.s_rice { background-position: -16425px 0px; }
660 | .emoji.s_ring { background-position: -16450px 0px; }
661 | .emoji.s_rocket { background-position: -16475px 0px; }
662 | .emoji.s_roller_coaster { background-position: -16500px 0px; }
663 | .emoji.s_rooster { background-position: -16525px 0px; }
664 | .emoji.s_rose { background-position: -16550px 0px; }
665 | .emoji.s_rotating_light { background-position: -16575px 0px; }
666 | .emoji.s_round_pushpin { background-position: -16600px 0px; }
667 | .emoji.s_rowboat { background-position: -16625px 0px; }
668 | .emoji.s_ru { background-position: -16650px 0px; }
669 | .emoji.s_rugby_football { background-position: -16675px 0px; }
670 | .emoji.s_runner { background-position: -16700px 0px; }
671 | .emoji.s_running { background-position: -16725px 0px; }
672 | .emoji.s_running_shirt_with_sash { background-position: -16750px 0px; }
673 | .emoji.s_sa { background-position: -16775px 0px; }
674 | .emoji.s_sagittarius { background-position: -16800px 0px; }
675 | .emoji.s_sake { background-position: -16825px 0px; }
676 | .emoji.s_sandal { background-position: -16850px 0px; }
677 | .emoji.s_santa { background-position: -16875px 0px; }
678 | .emoji.s_satellite { background-position: -16900px 0px; }
679 | .emoji.s_saxophone { background-position: -16925px 0px; }
680 | .emoji.s_school_satchel { background-position: -16950px 0px; }
681 | .emoji.s_school { background-position: -16975px 0px; }
682 | .emoji.s_scissors { background-position: -17000px 0px; }
683 | .emoji.s_scorpius { background-position: -17025px 0px; }
684 | .emoji.s_scream_cat { background-position: -17050px 0px; }
685 | .emoji.s_scream { background-position: -17075px 0px; }
686 | .emoji.s_scroll { background-position: -17100px 0px; }
687 | .emoji.s_seat { background-position: -17125px 0px; }
688 | .emoji.s_secret { background-position: -17150px 0px; }
689 | .emoji.s_see_no_evil { background-position: -17175px 0px; }
690 | .emoji.s_seedling { background-position: -17200px 0px; }
691 | .emoji.s_seven { background-position: -17225px 0px; }
692 | .emoji.s_shaved_ice { background-position: -17250px 0px; }
693 | .emoji.s_sheep { background-position: -17275px 0px; }
694 | .emoji.s_shell { background-position: -17300px 0px; }
695 | .emoji.s_ship { background-position: -17325px 0px; }
696 | .emoji.s_squirrel { background-position: -17350px 0px; }
697 | .emoji.s_shipit { background-position: -17375px 0px; }
698 | .emoji.s_tshirt { background-position: -17400px 0px; }
699 | .emoji.s_shirt { background-position: -17425px 0px; }
700 | .emoji.s_shower { background-position: -17450px 0px; }
701 | .emoji.s_signal_strength { background-position: -17475px 0px; }
702 | .emoji.s_six_pointed_star { background-position: -17500px 0px; }
703 | .emoji.s_six { background-position: -17525px 0px; }
704 | .emoji.s_ski { background-position: -17550px 0px; }
705 | .emoji.s_skull { background-position: -17575px 0px; }
706 | .emoji.s_sleeping { background-position: -17600px 0px; }
707 | .emoji.s_sleepy { background-position: -17625px 0px; }
708 | .emoji.s_slot_machine { background-position: -17650px 0px; }
709 | .emoji.s_small_blue_diamond { background-position: -17675px 0px; }
710 | .emoji.s_small_orange_diamond { background-position: -17700px 0px; }
711 | .emoji.s_small_red_triangle_down { background-position: -17725px 0px; }
712 | .emoji.s_small_red_triangle { background-position: -17750px 0px; }
713 | .emoji.s_smile_cat { background-position: -17775px 0px; }
714 | .emoji.s_smile { background-position: -17800px 0px; }
715 | .emoji.s_smiley_cat { background-position: -17825px 0px; }
716 | .emoji.s_smiley { background-position: -17850px 0px; }
717 | .emoji.s_smiling_imp { background-position: -17875px 0px; }
718 | .emoji.s_smirk_cat { background-position: -17900px 0px; }
719 | .emoji.s_smirk { background-position: -17925px 0px; }
720 | .emoji.s_smoking { background-position: -17950px 0px; }
721 | .emoji.s_snail { background-position: -17975px 0px; }
722 | .emoji.s_snake { background-position: -18000px 0px; }
723 | .emoji.s_snowboarder { background-position: -18025px 0px; }
724 | .emoji.s_snowflake { background-position: -18050px 0px; }
725 | .emoji.s_snowman { background-position: -18075px 0px; }
726 | .emoji.s_sob { background-position: -18100px 0px; }
727 | .emoji.s_soccer { background-position: -18125px 0px; }
728 | .emoji.s_soon { background-position: -18150px 0px; }
729 | .emoji.s_sos { background-position: -18175px 0px; }
730 | .emoji.s_sound { background-position: -18200px 0px; }
731 | .emoji.s_space_invader { background-position: -18225px 0px; }
732 | .emoji.s_spades { background-position: -18250px 0px; }
733 | .emoji.s_spaghetti { background-position: -18275px 0px; }
734 | .emoji.s_sparkle { background-position: -18300px 0px; }
735 | .emoji.s_sparkler { background-position: -18325px 0px; }
736 | .emoji.s_sparkles { background-position: -18350px 0px; }
737 | .emoji.s_sparkling_heart { background-position: -18375px 0px; }
738 | .emoji.s_speak_no_evil { background-position: -18400px 0px; }
739 | .emoji.s_speaker { background-position: -18425px 0px; }
740 | .emoji.s_speech_balloon { background-position: -18450px 0px; }
741 | .emoji.s_speedboat { background-position: -18475px 0px; }
742 | .emoji.s_star { background-position: -18500px 0px; }
743 | .emoji.s_star2 { background-position: -18525px 0px; }
744 | .emoji.s_stars { background-position: -18550px 0px; }
745 | .emoji.s_station { background-position: -18575px 0px; }
746 | .emoji.s_statue_of_liberty { background-position: -18600px 0px; }
747 | .emoji.s_steam_locomotive { background-position: -18625px 0px; }
748 | .emoji.s_stew { background-position: -18650px 0px; }
749 | .emoji.s_straight_ruler { background-position: -18675px 0px; }
750 | .emoji.s_strawberry { background-position: -18700px 0px; }
751 | .emoji.s_stuck_out_tongue_closed_eyes { background-position: -18725px 0px; }
752 | .emoji.s_stuck_out_tongue_winking_eye { background-position: -18750px 0px; }
753 | .emoji.s_stuck_out_tongue { background-position: -18775px 0px; }
754 | .emoji.s_sun_with_face { background-position: -18800px 0px; }
755 | .emoji.s_sunflower { background-position: -18825px 0px; }
756 | .emoji.s_sunglasses { background-position: -18850px 0px; }
757 | .emoji.s_sunny { background-position: -18875px 0px; }
758 | .emoji.s_sunrise_over_mountains { background-position: -18900px 0px; }
759 | .emoji.s_sunrise { background-position: -18925px 0px; }
760 | .emoji.s_surfer { background-position: -18950px 0px; }
761 | .emoji.s_sushi { background-position: -18975px 0px; }
762 | .emoji.s_suspect { background-position: -19000px 0px; }
763 | .emoji.s_suspension_railway { background-position: -19025px 0px; }
764 | .emoji.s_sweat_drops { background-position: -19050px 0px; }
765 | .emoji.s_sweat_smile { background-position: -19075px 0px; }
766 | .emoji.s_sweat { background-position: -19100px 0px; }
767 | .emoji.s_sweet_potato { background-position: -19125px 0px; }
768 | .emoji.s_swimmer { background-position: -19150px 0px; }
769 | .emoji.s_symbols { background-position: -19175px 0px; }
770 | .emoji.s_syringe { background-position: -19200px 0px; }
771 | .emoji.s_tada { background-position: -19225px 0px; }
772 | .emoji.s_tanabata_tree { background-position: -19250px 0px; }
773 | .emoji.s_tangerine { background-position: -19275px 0px; }
774 | .emoji.s_taurus { background-position: -19300px 0px; }
775 | .emoji.s_taxi { background-position: -19325px 0px; }
776 | .emoji.s_tea { background-position: -19350px 0px; }
777 | .emoji.s_telephone_receiver { background-position: -19375px 0px; }
778 | .emoji.s_telescope { background-position: -19400px 0px; }
779 | .emoji.s_tennis { background-position: -19425px 0px; }
780 | .emoji.s_tent { background-position: -19450px 0px; }
781 | .emoji.s_thought_balloon { background-position: -19475px 0px; }
782 | .emoji.s_three { background-position: -19500px 0px; }
783 | .emoji.s_ticket { background-position: -19525px 0px; }
784 | .emoji.s_tiger { background-position: -19550px 0px; }
785 | .emoji.s_tiger2 { background-position: -19575px 0px; }
786 | .emoji.s_tired_face { background-position: -19600px 0px; }
787 | .emoji.s_tm { background-position: -19625px 0px; }
788 | .emoji.s_toilet { background-position: -19650px 0px; }
789 | .emoji.s_tokyo_tower { background-position: -19675px 0px; }
790 | .emoji.s_tomato { background-position: -19700px 0px; }
791 | .emoji.s_tongue { background-position: -19725px 0px; }
792 | .emoji.s_top { background-position: -19750px 0px; }
793 | .emoji.s_tophat { background-position: -19775px 0px; }
794 | .emoji.s_tractor { background-position: -19800px 0px; }
795 | .emoji.s_traffic_light { background-position: -19825px 0px; }
796 | .emoji.s_train2 { background-position: -19850px 0px; }
797 | .emoji.s_tram { background-position: -19875px 0px; }
798 | .emoji.s_triangular_flag_on_post { background-position: -19900px 0px; }
799 | .emoji.s_triangular_ruler { background-position: -19925px 0px; }
800 | .emoji.s_trident { background-position: -19950px 0px; }
801 | .emoji.s_triumph { background-position: -19975px 0px; }
802 | .emoji.s_trolleybus { background-position: -20000px 0px; }
803 | .emoji.s_trollface { background-position: -20025px 0px; }
804 | .emoji.s_trophy { background-position: -20050px 0px; }
805 | .emoji.s_tropical_drink { background-position: -20075px 0px; }
806 | .emoji.s_tropical_fish { background-position: -20100px 0px; }
807 | .emoji.s_truck { background-position: -20125px 0px; }
808 | .emoji.s_trumpet { background-position: -20150px 0px; }
809 | .emoji.s_tulip { background-position: -20175px 0px; }
810 | .emoji.s_turtle { background-position: -20200px 0px; }
811 | .emoji.s_tv { background-position: -20225px 0px; }
812 | .emoji.s_twisted_rightwards_arrows { background-position: -20250px 0px; }
813 | .emoji.s_two_hearts { background-position: -20275px 0px; }
814 | .emoji.s_two_men_holding_hands { background-position: -20300px 0px; }
815 | .emoji.s_two_women_holding_hands { background-position: -20325px 0px; }
816 | .emoji.s_two { background-position: -20350px 0px; }
817 | .emoji.s_u6e80 { background-position: -20375px 0px; }
818 | .emoji.s_u7a7a { background-position: -20400px 0px; }
819 | .emoji.s_u55b6 { background-position: -20425px 0px; }
820 | .emoji.s_u5272 { background-position: -20450px 0px; }
821 | .emoji.s_u5408 { background-position: -20475px 0px; }
822 | .emoji.s_u6307 { background-position: -20500px 0px; }
823 | .emoji.s_u6708 { background-position: -20525px 0px; }
824 | .emoji.s_u6709 { background-position: -20550px 0px; }
825 | .emoji.s_u7121 { background-position: -20575px 0px; }
826 | .emoji.s_u7533 { background-position: -20600px 0px; }
827 | .emoji.s_u7981 { background-position: -20625px 0px; }
828 | .emoji.s_umbrella { background-position: -20650px 0px; }
829 | .emoji.s_unamused { background-position: -20675px 0px; }
830 | .emoji.s_underage { background-position: -20700px 0px; }
831 | .emoji.s_unlock { background-position: -20725px 0px; }
832 | .emoji.s_up { background-position: -20750px 0px; }
833 | .emoji.s_us { background-position: -20775px 0px; }
834 | .emoji.s_v { background-position: -20800px 0px; }
835 | .emoji.s_vertical_traffic_light { background-position: -20825px 0px; }
836 | .emoji.s_vhs { background-position: -20850px 0px; }
837 | .emoji.s_vibration_mode { background-position: -20875px 0px; }
838 | .emoji.s_video_camera { background-position: -20900px 0px; }
839 | .emoji.s_video_game { background-position: -20925px 0px; }
840 | .emoji.s_violin { background-position: -20950px 0px; }
841 | .emoji.s_virgo { background-position: -20975px 0px; }
842 | .emoji.s_volcano { background-position: -21000px 0px; }
843 | .emoji.s_vs { background-position: -21025px 0px; }
844 | .emoji.s_walking { background-position: -21050px 0px; }
845 | .emoji.s_waning_crescent_moon { background-position: -21075px 0px; }
846 | .emoji.s_waning_gibbous_moon { background-position: -21100px 0px; }
847 | .emoji.s_warning { background-position: -21125px 0px; }
848 | .emoji.s_watch { background-position: -21150px 0px; }
849 | .emoji.s_water_buffalo { background-position: -21175px 0px; }
850 | .emoji.s_watermelon { background-position: -21200px 0px; }
851 | .emoji.s_wave { background-position: -21225px 0px; }
852 | .emoji.s_wavy_dash { background-position: -21250px 0px; }
853 | .emoji.s_waxing_crescent_moon { background-position: -21275px 0px; }
854 | .emoji.s_wc { background-position: -21300px 0px; }
855 | .emoji.s_weary { background-position: -21325px 0px; }
856 | .emoji.s_wedding { background-position: -21350px 0px; }
857 | .emoji.s_whale { background-position: -21375px 0px; }
858 | .emoji.s_whale2 { background-position: -21400px 0px; }
859 | .emoji.s_wheelchair { background-position: -21425px 0px; }
860 | .emoji.s_white_check_mark { background-position: -21450px 0px; }
861 | .emoji.s_white_circle { background-position: -21475px 0px; }
862 | .emoji.s_white_flower { background-position: -21500px 0px; }
863 | .emoji.s_white_large_square { background-position: -21525px 0px; }
864 | .emoji.s_white_medium_small_square { background-position: -21550px 0px; }
865 | .emoji.s_white_medium_square { background-position: -21575px 0px; }
866 | .emoji.s_white_small_square { background-position: -21600px 0px; }
867 | .emoji.s_white_square_button { background-position: -21625px 0px; }
868 | .emoji.s_wind_chime { background-position: -21650px 0px; }
869 | .emoji.s_wine_glass { background-position: -21675px 0px; }
870 | .emoji.s_wink { background-position: -21700px 0px; }
871 | .emoji.s_wolf { background-position: -21725px 0px; }
872 | .emoji.s_woman { background-position: -21750px 0px; }
873 | .emoji.s_womans_clothes { background-position: -21775px 0px; }
874 | .emoji.s_womans_hat { background-position: -21800px 0px; }
875 | .emoji.s_womens { background-position: -21825px 0px; }
876 | .emoji.s_worried { background-position: -21850px 0px; }
877 | .emoji.s_wrench { background-position: -21875px 0px; }
878 | .emoji.s_x { background-position: -21900px 0px; }
879 | .emoji.s_yellow_heart { background-position: -21925px 0px; }
880 | .emoji.s_yen { background-position: -21950px 0px; }
881 | .emoji.s_yum { background-position: -21975px 0px; }
882 | .emoji.s_zap { background-position: -22000px 0px; }
883 | .emoji.s_zero { background-position: -22025px 0px; }
884 | .emoji.s_zzz { background-position: -22050px 0px; }
885 | .emoji.s_fu { background-position: -22075px 0px; }
886 |
--------------------------------------------------------------------------------
/dist/assets/emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin940726/minesweeper/ab9b0dd2d80e66389ed347455787934d0807799a/dist/assets/emoji.png
--------------------------------------------------------------------------------
/dist/ga.js:
--------------------------------------------------------------------------------
1 | if (window.location.protocol != "https:") {
2 | window.location.href = "https:" + window.location.href.substring(window.location.protocol.length);
3 | }
4 |
5 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
6 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
7 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
8 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
9 |
10 | ga('create', 'UA-75983216-1', 'auto');
11 | ga('send', 'pageview');
12 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Minesweeper
8 |
9 |
10 |
11 |
12 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | var webpackCfg = require('./webpack.config');
2 |
3 | module.exports = function(config) {
4 | config.set({
5 | basePath: '',
6 | browsers: [ 'PhantomJS' ],
7 | files: [
8 | 'test/loadtests.js'
9 | ],
10 | port: 8080,
11 | captureTimeout: 60000,
12 | frameworks: [ 'mocha', 'chai' ],
13 | client: {
14 | mocha: {}
15 | },
16 | singleRun: true,
17 | reporters: [ 'mocha', 'coverage' ],
18 | preprocessors: {
19 | 'test/loadtests.js': [ 'webpack', 'sourcemap' ]
20 | },
21 | webpack: webpackCfg,
22 | webpackServer: {
23 | noInfo: true
24 | },
25 | coverageReporter: {
26 | dir: 'coverage/',
27 | reporters: [
28 | { type: 'html' },
29 | { type: 'text' }
30 | ]
31 | }
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "minesweeper",
3 | "private": true,
4 | "version": "0.0.1",
5 | "description": "YOUR DESCRIPTION - Generated by generator-react-webpack",
6 | "main": "",
7 | "scripts": {
8 | "clean": "rimraf dist/*",
9 | "copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist",
10 | "dist": "npm run copy & webpack --env=dist",
11 | "lint": "eslint ./src",
12 | "posttest": "npm run lint",
13 | "release:major": "npm version major && npm publish && git push --follow-tags",
14 | "release:minor": "npm version minor && npm publish && git push --follow-tags",
15 | "release:patch": "npm version patch && npm publish && git push --follow-tags",
16 | "serve": "node server.js --env=dev",
17 | "serve:dist": "node server.js --env=dist",
18 | "start": "node server.js --env=dev",
19 | "test": "karma start",
20 | "test:watch": "karma start --autoWatch=true --singleRun=false"
21 | },
22 | "repository": "",
23 | "keywords": [],
24 | "author": "Your name here",
25 | "devDependencies": {
26 | "babel-core": "^6.0.0",
27 | "babel-eslint": "^6.0.0",
28 | "babel-loader": "^6.0.0",
29 | "babel-polyfill": "^6.3.14",
30 | "babel-preset-es2015": "^6.0.15",
31 | "babel-preset-react": "^6.0.15",
32 | "bower-webpack-plugin": "^0.1.9",
33 | "chai": "^3.2.0",
34 | "copyfiles": "^0.2.1",
35 | "css-loader": "^0.23.0",
36 | "eslint": "^2.2.0",
37 | "eslint-loader": "^1.0.0",
38 | "eslint-plugin-react": "^4.0.0",
39 | "file-loader": "^0.8.4",
40 | "glob": "^7.0.0",
41 | "isparta-instrumenter-loader": "^1.0.0",
42 | "karma": "^0.13.9",
43 | "karma-chai": "^0.1.0",
44 | "karma-coverage": "^0.5.3",
45 | "karma-mocha": "^0.2.0",
46 | "karma-mocha-reporter": "^2.0.0",
47 | "karma-phantomjs-launcher": "^1.0.0",
48 | "karma-sourcemap-loader": "^0.3.5",
49 | "karma-webpack": "^1.7.0",
50 | "minimist": "^1.2.0",
51 | "mocha": "^2.2.5",
52 | "null-loader": "^0.1.1",
53 | "open": "0.0.5",
54 | "phantomjs-prebuilt": "^2.0.0",
55 | "react-addons-test-utils": "^0.14.0",
56 | "react-hot-loader": "^1.2.9",
57 | "rimraf": "^2.4.3",
58 | "style-loader": "^0.13.0",
59 | "url-loader": "^0.5.6",
60 | "webpack": "^1.12.0",
61 | "webpack-dev-server": "^1.12.0",
62 | "sass-loader": "^3.1.2",
63 | "node-sass": "^3.4.2",
64 | "postcss": "^5.0.11",
65 | "postcss-loader": "^0.8.0"
66 | },
67 | "dependencies": {
68 | "cookies-js": "^1.2.2",
69 | "core-js": "^2.0.0",
70 | "normalize.css": "^4.0.0",
71 | "radium": "^0.17.1",
72 | "react": "^15.0.0",
73 | "react-dom": "^15.0.0",
74 | "react-redux": "^4.4.1",
75 | "react-sticky-state": "^1.3.7",
76 | "redux": "^3.3.1",
77 | "redux-actions": "^0.9.1",
78 | "redux-thunk": "^2.0.1",
79 | "reselect": "^2.2.1",
80 | "rref": "0.0.1"
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin940726/minesweeper/ab9b0dd2d80e66389ed347455787934d0807799a/screenshot.png
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /*eslint no-console:0 */
2 | 'use strict';
3 | require('core-js/fn/object/assign');
4 | const webpack = require('webpack');
5 | const WebpackDevServer = require('webpack-dev-server');
6 | const config = require('./webpack.config');
7 | const open = require('open');
8 |
9 | new WebpackDevServer(webpack(config), config.devServer)
10 | .listen(config.port, 'localhost', (err) => {
11 | if (err) {
12 | console.log(err);
13 | }
14 | console.log('Listening at localhost:' + config.port);
15 | console.log('Opening your system browser...');
16 | // open('http://localhost:' + config.port + '/webpack-dev-server/');
17 | });
18 |
--------------------------------------------------------------------------------
/src/actions/index.js:
--------------------------------------------------------------------------------
1 | import { createAction } from 'redux-actions';
2 | import { BlockRecord } from '../minesweeper';
3 | import Cookie from 'cookies-js';
4 |
5 | export const setGame = createAction('SET_GAME');
6 |
7 | export const toggleLoading = createAction('TOGGLE_LOADING');
8 |
9 | export const handleClick = (row, col) => (
10 | (dispatch, getState) => {
11 | if (getState().mw.status === 'ready' && getState().mw.checkIsSolvable) {
12 | Promise.resolve(dispatch(toggleLoading()))
13 | .then(() => getState().mw.singleClick({ row, col }))
14 | .then(mw => dispatch(setGame(mw)))
15 | .then(() => dispatch(toggleLoading()));
16 | }
17 | else {
18 | getState().mw.singleClick({ row, col })
19 | .then(mw => dispatch(setGame(mw)));
20 | }
21 | }
22 | );
23 |
24 | export const handleFlag = (row, col) => (
25 | (dispatch, getState) => {
26 | getState().mw.rightClick({ row, col }).then(mw => {
27 | dispatch(setGame(mw));
28 | });
29 | }
30 | );
31 |
32 | export const toggleMode = createAction('TOGGLE_MODE');
33 |
34 | export const restartGame = createAction('RESTART_GAME');
35 |
36 | export const updateTime = createAction('UPDATE_TIME');
37 |
38 | export const saveConfig = createAction('SAVE_CONFIG', (value, target) => {
39 | Cookie.set(target, parseInt(value));
40 | return { value: parseInt(value) || 0, target };
41 | });
42 |
43 | export const togglePanel = createAction('TOGGLE_PANEL');
44 |
45 | export const toggleFlagMode = createAction('TOGGLE_FLAG_MODE', () => {
46 | Cookie.set("flagMode", Cookie.get("flagMode") === "true" ? "false" : "true");
47 | });
48 |
49 | export const toggleCheckIsSolvable = createAction('TOGGLE_CHECK_IS_SOLVABLE', () => {
50 | Cookie.set("checkIsSolvable", Cookie.get("checkIsSolvable") === "true" ? "false" : "true");
51 | });
52 |
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Weeper from '../containers/Weeper';
3 |
4 | const App = () => (
5 |
6 | );
7 |
8 | export default App;
9 |
--------------------------------------------------------------------------------
/src/components/ConfigBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Radium from 'radium';
3 | import hasFont from '../hasAppleColorEmoji';
4 |
5 | const style = {
6 | base: {
7 | display: "flex",
8 | height: "30px",
9 | lineHeight: "30px",
10 | marginBottom: "-10px",
11 | justifyContent: "space-between",
12 | alignItems: "center"
13 | },
14 | btn: {
15 | textDecoration: "none",
16 | fontFamily: "'AppleColorEmoji', 'Roboto', sans-serif",
17 | fontSize: "18px",
18 | margin: "0 4px",
19 | width: "30px",
20 | height: "30px",
21 | lineHeight: "18px",
22 | padding: "5px 5px 5px 4px",
23 | boxSizing: "border-box",
24 | backgroundColor: "#FFF",
25 | transition: "background-color 0.2s ease-out",
26 |
27 | ":hover": {
28 | backgroundColor: "#EEE"
29 | }
30 | },
31 |
32 | sizeHack: {
33 | width: "24px",
34 | height: "24px",
35 | margin: "3px",
36 | transition: "background-color 0.2s ease-out, box-shadow 0.2s ease-out",
37 | ":hover": {
38 | backgroundColor: "#EEE",
39 | boxShadow: "0 0 0 3px #EEE"
40 | }
41 | }
42 | };
43 |
44 | const ConfigBar = ({ mw, mode, togglePanel, toggleMode }) => (
45 |
51 | );
52 |
53 | export default Radium(ConfigBar);
54 |
--------------------------------------------------------------------------------
/src/components/ConfigPanel.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Radium from 'radium';
3 | import hasFont from '../hasAppleColorEmoji';
4 |
5 | const style = {
6 | base: {
7 | position: "fixed",
8 | display: "flex",
9 | flexDirection: "column",
10 | top: "55%",
11 | left: "50%",
12 | transform: "translate(-50%, -50%)",
13 | boxShadow: "0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)",
14 | padding: "40px",
15 | backgroundColor: "#FFF",
16 | zIndex: "-1",
17 | opacity: "0",
18 | transition: "top 0.3s ease-out, opacity 0.3s ease-out, z-index 0.3s ease-out"
19 | },
20 | show: {
21 | zIndex: "10",
22 | opacity: "1",
23 | top: "50%"
24 | },
25 | label: {
26 | fontSize: "20px",
27 | display: "flex",
28 | justifyContent: "space-between",
29 | alignItems: "center",
30 | marginBottom: "15px",
31 | fontFamily: "'AppleColorEmoji', 'Roboto', sans-serif",
32 | width: "100%"
33 | },
34 | input: {
35 | flexBasis: "50px",
36 | width: "120px",
37 | height: "30px",
38 | marginLeft: "20px",
39 | fontFamily: "'Roboto', sans-serif",
40 | padding: "5px 10px",
41 | fontSize: "16px",
42 | boxSizing: "border-box",
43 | textAlign: "center",
44 | color: "#000"
45 | },
46 | submit: {
47 | padding: "5px 10px",
48 | border: "1px solid #EEE",
49 | backgroundColor: "#FFF",
50 | display: "inline-block",
51 | fontFamily: "'AppleColorEmoji', 'Roboto', sans-serif",
52 | fontSize: "20px",
53 | cursor: "pointer",
54 | width: "100%",
55 | transition: "border 0.2s ease-out",
56 | ":hover": {
57 | border: "1px solid #CCC"
58 | }
59 | },
60 |
61 | upperGroup: {
62 | display: "flex",
63 | alignItems: "center",
64 | marginBottom: "10px"
65 | },
66 | checkbox: {
67 | padding: "5px 5px 5px 3px",
68 | backgroundColor: "#FFF",
69 | transition: "background-color 0.2s ease-out",
70 | width: "30px",
71 | boxSizing: "border-box",
72 | height: "30px",
73 | lineHeight: "22px",
74 | cursor: "pointer",
75 | marginBottom: "0px",
76 | fontFamily: "'AppleColorEmoji', 'Roboto', sans-serif",
77 | ":hover": {
78 | backgroundColor: "#EEE"
79 | }
80 | },
81 | line: {
82 | height: "30px",
83 | width: "1px",
84 | backgroundColor: "#EEE",
85 | margin: "0px 10px"
86 | },
87 | level: {
88 | padding: "5px",
89 | lineHeight: "20px"
90 | },
91 | levelHack: {
92 | width: "24px",
93 | height: "24px",
94 | margin: "3px",
95 | transition: "background-color 0.2s ease-out, box-shadow 0.2s ease-out",
96 | ":hover": {
97 | backgroundColor: "#EEE",
98 | boxShadow: "0 0 0 3px #EEE"
99 | }
100 | }
101 | };
102 |
103 | const ConfigPanel = ({ show, rows, cols, mines, flagMode, checkIsSolvable, saveConfig, saveAllConfig, togglePanel, toggleFlagMode, toggleCheckIsSolvable }) => (
104 |
136 | );
137 |
138 | export default Radium(ConfigPanel);
139 |
--------------------------------------------------------------------------------
/src/components/ControlBar.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Radium, { keyframes } from 'radium';
3 | import Sticky from 'react-sticky-state';
4 | import hasFont from '../hasAppleColorEmoji';
5 |
6 | const giggle = keyframes({
7 | "0%": {
8 | transform: "translateY(0px)"
9 | },
10 | "3%": {
11 | transform: "translateY(-2px)"
12 | },
13 | "9%": {
14 | transform: "translateY(2px)"
15 | },
16 | "15%": {
17 | transform: "translateY(-2px)"
18 | },
19 | "21%": {
20 | transform: "translateY(2px)"
21 | },
22 | "24%": {
23 | transform: "translateY(0px)"
24 | },
25 | "100%": {
26 | transform: "translateY(0px)"
27 | }
28 | });
29 |
30 | const style = {
31 | base: {
32 | display: "flex",
33 | justifyContent: "space-between",
34 | alignItems: "center",
35 | position: "relative",
36 | height: "40px",
37 | marginBottom: "5px",
38 | border: "1px solid #EEE"
39 | },
40 | btn: {
41 | position: "absolute",
42 | left: 0,
43 | right: 0,
44 | top: 0,
45 | bottom: 0,
46 | margin: "auto",
47 | textDecoration: "none",
48 | padding: "5px 5px 5px 3px",
49 | fontSize: "20px",
50 | height: "36px",
51 | width: "36px",
52 | lineHeight: "26px",
53 | boxSizing: "border-box",
54 | textAlign: "center",
55 | transition: "background-color 0.2s ease-out",
56 | fontFamily: "'AppleColorEmoji', 'Roboto', sans-serif",
57 |
58 | ":hover": {
59 | backgroundColor: "#F0F0F0"
60 | }
61 | },
62 | text: {
63 | fontSize: "18px",
64 | margin: "0 10px",
65 | lineHeight: "18px",
66 | letterSpacing: "0.1em"
67 | },
68 | emoji: {
69 | fontFamily: "'AppleColorEmoji', 'Roboto', sans-serif"
70 | },
71 | ani: {
72 | animation: "x 1.5s ease-in-out infinite",
73 | animationName: giggle
74 | }
75 | };
76 |
77 | const TimePass = Radium(React.createClass({
78 | componentDidMount() {
79 | this.props.mw.on("timeupdated", timePass => {
80 | this.props.updateTime(timePass);
81 | });
82 | },
83 |
84 | render() {
85 | return (
86 |
87 | ⏱ {this.props.timePass}
88 |
89 | );
90 | }
91 | }));
92 |
93 | const ControlBar = ({ minesRemaining, display, text, restartGame, timePass, mw, updateTime }) => (
94 |
108 | );
109 |
110 | export default Radium(ControlBar);
111 |
--------------------------------------------------------------------------------
/src/components/Grid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Radium from 'radium';
3 | import hasFont from '../hasAppleColorEmoji';
4 |
5 | const style = {
6 | base: {
7 | display: "flex",
8 | position: "relative",
9 | height: "28px",
10 | width: "30px",
11 | alignItems: "center",
12 | justifyContent: "center",
13 | fontSize: "20px",
14 | padding: "3px 3px 5px 3px",
15 | lineHeight: "28px", // fuck you, waste 4 hours on this shit...
16 | cursor: "default",
17 | transition: "background-color 0.1s ease-out",
18 | backgroundColor: "#FFF",
19 | backgroundClip: "content-box",
20 | userSelect: "none"
21 | },
22 | after: {
23 | border: "1px solid #EEE",
24 | position: "absolute",
25 | boxSizing: "border-box",
26 | top: 0,
27 | left: 0,
28 | right: 0,
29 | bottom: 0,
30 | margin: "3px"
31 | },
32 |
33 | hidden: {
34 | backgroundColor: "#EEE",
35 | padding: "3px",
36 | height: "30px",
37 | ":hover": {
38 | backgroundColor: "#F5F5F5"
39 | }
40 | },
41 | emoji: {
42 | fontFamily: "'AppleColorEmoji', 'Roboto', sans-serif"
43 | }
44 | };
45 |
46 | const Grid = ({ row, col, block, handleClick, handleFlag }) => (
47 | {
49 | e.preventDefault();
50 | handleFlag(row, col);
51 | }}
52 | style={[
53 | style.base,
54 | block.hidden && style.hidden
55 | ]}
56 | >
57 |
58 | {block.flag ? (🚩) :
59 | (block.hidden ? " " :
60 | (block.type === "mine" ? (💣) :
61 | (block.mines || " ")
62 | )
63 | )}
64 |
65 | );
66 |
67 | export default Radium(Grid);
68 |
--------------------------------------------------------------------------------
/src/components/Weeper.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Grid from '../containers/Grid';
3 | import ControlBar from '../containers/ControlBar';
4 | import ConfigBar from '../containers/ConfigBar';
5 | import ConfigPanel from '../containers/ConfigPanel';
6 | import { BlockRecord } from '../minesweeper';
7 | import Radium from 'radium';
8 | import hasFont from '../hasAppleColorEmoji';
9 |
10 | const style = {
11 | base: {
12 | display: "block",
13 | position: "absolute",
14 | left: "50%",
15 | top: "50%",
16 | transform: "translate(-50%, -50%)",
17 | padding: "20px 30px",
18 | boxShadow: "0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)",
19 | backgroundColor: "#FFF"
20 | },
21 | row: {
22 | display: "flex",
23 | justifyContent: "center",
24 | alignItems: "center",
25 | flexDirection: "row",
26 | backgroundColor: "#FFF"
27 | },
28 | loading: {
29 | position: "relative",
30 | top: 0,
31 | left: 0,
32 | right: 0,
33 | bottom: 0,
34 | height: "100%",
35 | width: "100%",
36 | filter: "blur(0px)",
37 | transition: "filter 0.5s ease-out"
38 | },
39 | isLoading: {
40 | filter: "blur(5px)"
41 | },
42 |
43 | loadingBackground: {
44 | position: "absolute",
45 | top: 0,
46 | left: 0,
47 | right: 0,
48 | bottom: 0,
49 | height: "100%",
50 | width: "100%"
51 | },
52 | loadingIcon: {
53 | display: "block",
54 | fontFamily: "'AppleColorEmoji', 'Roboto', sans-serif",
55 | fontSize: "30px",
56 | textAlign: "center",
57 | position: "absolute",
58 | top: "50%",
59 | left: 0,
60 | right: 0,
61 | margin: "auto",
62 | transform: "translateY(-50%)"
63 | }
64 | };
65 |
66 | let Row = ({ cols, row }) => (
67 |
68 | {new Array(cols).fill(0).map((cur, col) => {
69 | return (
70 |
74 |
75 | );
76 | })}
77 |
78 | );
79 | Row = Radium(Row);
80 |
81 | let LoadingWrapper = ({ isLoading, children }) => (
82 |
83 | {children}
84 |
85 |
86 | );
87 | LoadingWrapper = Radium(LoadingWrapper);
88 |
89 | let LoadingIcon = ({ isLoading }) => (
90 | ⏳
91 | );
92 | LoadingIcon = Radium(LoadingIcon);
93 |
94 | const Weeper = ({ rows, cols, isLoading }) => (
95 |
96 |
97 |
98 | {new Array(rows).fill(0).map((cur, row) => (
99 |
100 | ))}
101 |
102 |
103 | { isLoading && () }
104 |
105 | { isLoading && () }
106 |
107 | );
108 |
109 | export default Radium(Weeper);
110 |
--------------------------------------------------------------------------------
/src/containers/ConfigBar.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux';
2 | import { togglePanel, toggleMode } from '../actions';
3 | import configBar from '../components/ConfigBar';
4 |
5 | const mapStateToProps = (state) => ({
6 | mode: state.mw.mode
7 | });
8 |
9 | const mapDispatchToProps = (dispatch) => ({
10 | togglePanel: (e) => {
11 | e.preventDefault();
12 | dispatch(togglePanel());
13 | },
14 |
15 | toggleMode: (e) => {
16 | e && e.preventDefault();
17 | dispatch(toggleMode());
18 | }
19 | });
20 |
21 | const ConfigBar = connect(mapStateToProps, mapDispatchToProps)(configBar);
22 |
23 | export default ConfigBar;
24 |
--------------------------------------------------------------------------------
/src/containers/ConfigPanel.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux';
2 | import configPanel from '../components/ConfigPanel';
3 | import { saveConfig, togglePanel, toggleFlagMode, toggleCheckIsSolvable } from '../actions';
4 |
5 | const mapStateToProps = (state) => ({
6 | rows: state.config.rows,
7 | cols: state.config.cols,
8 | mines: state.config.mines,
9 | show: state.config.show,
10 | flagMode: state.config.flagMode,
11 | checkIsSolvable: state.config.checkIsSolvable
12 | });
13 |
14 | const mapDispatchToProps = (dispatch) => ({
15 | saveConfig: (e, target) => {
16 | dispatch(saveConfig(e.target.value, target));
17 | },
18 |
19 | togglePanel: (e) => {
20 | e.preventDefault();
21 | dispatch(togglePanel());
22 | },
23 |
24 | toggleFlagMode: () => {
25 | dispatch(toggleFlagMode());
26 | },
27 | toggleCheckIsSolvable: () => {
28 | dispatch(toggleCheckIsSolvable());
29 | },
30 |
31 | saveAllConfig: (rows, cols, mines) => {
32 | dispatch(saveConfig(rows, "rows"));
33 | dispatch(saveConfig(cols, "cols"));
34 | dispatch(saveConfig(mines, "mines"));
35 | }
36 | });
37 |
38 | const ConfigPanel = connect(mapStateToProps, mapDispatchToProps)(configPanel);
39 |
40 | export default ConfigPanel;
41 |
--------------------------------------------------------------------------------
/src/containers/ControlBar.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux';
2 | import controlBar from '../components/ControlBar';
3 | import { createSelector } from 'reselect';
4 | import { restartGame, updateTime, updateHighScore } from '../actions';
5 |
6 | const statusSelector = state => state.mw.status;
7 |
8 | const textSelector = createSelector(
9 | statusSelector,
10 | status => status === "win" ? "grin" : (status === "lose" ? "cry" : "blush")
11 | );
12 |
13 | const minesRemainingSelector = createSelector(
14 | state => state.mw.minesRemaining,
15 | minesRemaining => minesRemaining
16 | );
17 |
18 | const timePassSelector = createSelector(
19 | state => state.timePass,
20 | timePass => "000".substring(0, 3 - timePass.toString().length) + timePass.toString()
21 | );
22 |
23 | const mapStateToProps = (state) => ({
24 | minesRemaining: minesRemainingSelector(state),
25 | text: textSelector(state),
26 | timePass: timePassSelector(state),
27 | mw: state.mw
28 | });
29 |
30 | const mapDispatchToProps = (dispatch) => ({
31 | restartGame: (e) => {
32 | e.preventDefault();
33 | dispatch(restartGame());
34 | },
35 |
36 | updateTime: timePass => {
37 | dispatch(updateTime(timePass));
38 | }
39 | });
40 |
41 | const ControlBar = connect(mapStateToProps, mapDispatchToProps)(controlBar);
42 |
43 | export default ControlBar;
44 |
--------------------------------------------------------------------------------
/src/containers/Grid.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux';
2 | import grid from '../components/Grid';
3 | import { BlockRecord } from '../minesweeper';
4 | import { handleClick, handleFlag, setGame } from '../actions';
5 | import { createSelector } from 'reselect';
6 |
7 | const blockSelector = createSelector(
8 | state => state.mw.blocks,
9 | (state, ownProps) => ownProps,
10 | (blocks, ownProps) => blocks.get(new BlockRecord({
11 | row: ownProps.row,
12 | col: ownProps.col
13 | }))
14 | );
15 |
16 | const mapStateToProps = (state, ownProps) => ({
17 | block: blockSelector(state, ownProps)
18 | });
19 |
20 | const mapDispatchToProps = (dispatch, ownProps) => ({
21 | handleClick: (row, col) => {
22 | dispatch(handleClick(row, col));
23 | },
24 |
25 | handleFlag: (row, col) => {
26 | dispatch(handleFlag(row, col));
27 | }
28 | });
29 |
30 | const Grid = connect(mapStateToProps, mapDispatchToProps)(grid);
31 |
32 | export default Grid;
33 |
--------------------------------------------------------------------------------
/src/containers/Weeper.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux';
2 | import weeper from '../components/Weeper';
3 | import { handleClick, handleFlag } from '../actions';
4 |
5 | const mapStateToProps = (state) => ({
6 | rows: state.mw.rows,
7 | cols: state.mw.cols,
8 | isLoading: state.config.isLoading
9 | });
10 |
11 | const Weeper = connect(mapStateToProps)(weeper);
12 |
13 | export default Weeper;
14 |
--------------------------------------------------------------------------------
/src/emoji.css:
--------------------------------------------------------------------------------
1 | .emoji { background: url('./emoji.png') no-repeat -25px -25px; width: 24px; height: 24px; color: transparent; }
2 | .emoji.s_-1 { background-position: 0px 0px; }
3 | .emoji.s_thumbsdown { background-position: -25px 0px; }
4 | .emoji.s_1 { background-position: -50px 0px; }
5 | .emoji.s_thumbsup { background-position: -75px 0px; }
6 | .emoji.s_8ball { background-position: -100px 0px; }
7 | .emoji.s_100 { background-position: -125px 0px; }
8 | .emoji.s_1234 { background-position: -150px 0px; }
9 | .emoji.s_a { background-position: -175px 0px; }
10 | .emoji.s_ab { background-position: -200px 0px; }
11 | .emoji.s_abc { background-position: -225px 0px; }
12 | .emoji.s_abcd { background-position: -250px 0px; }
13 | .emoji.s_accept { background-position: -275px 0px; }
14 | .emoji.s_aerial_tramway { background-position: -300px 0px; }
15 | .emoji.s_airplane { background-position: -325px 0px; }
16 | .emoji.s_alarm_clock { background-position: -350px 0px; }
17 | .emoji.s_alien { background-position: -375px 0px; }
18 | .emoji.s_ambulance { background-position: -400px 0px; }
19 | .emoji.s_anchor { background-position: -425px 0px; }
20 | .emoji.s_angel { background-position: -450px 0px; }
21 | .emoji.s_anger { background-position: -475px 0px; }
22 | .emoji.s_angry { background-position: -500px 0px; }
23 | .emoji.s_anguished { background-position: -525px 0px; }
24 | .emoji.s_ant { background-position: -550px 0px; }
25 | .emoji.s_apple { background-position: -575px 0px; }
26 | .emoji.s_aquarius { background-position: -600px 0px; }
27 | .emoji.s_aries { background-position: -625px 0px; }
28 | .emoji.s_arrow_backward { background-position: -650px 0px; }
29 | .emoji.s_arrow_double_down { background-position: -675px 0px; }
30 | .emoji.s_arrow_double_up { background-position: -700px 0px; }
31 | .emoji.s_arrow_down_small { background-position: -725px 0px; }
32 | .emoji.s_arrow_down { background-position: -750px 0px; }
33 | .emoji.s_arrow_forward { background-position: -775px 0px; }
34 | .emoji.s_arrow_heading_down { background-position: -800px 0px; }
35 | .emoji.s_arrow_heading_up { background-position: -825px 0px; }
36 | .emoji.s_arrow_left { background-position: -850px 0px; }
37 | .emoji.s_arrow_lower_left { background-position: -875px 0px; }
38 | .emoji.s_arrow_lower_right { background-position: -900px 0px; }
39 | .emoji.s_arrow_right_hook { background-position: -925px 0px; }
40 | .emoji.s_arrow_right { background-position: -950px 0px; }
41 | .emoji.s_arrow_up_down { background-position: -975px 0px; }
42 | .emoji.s_arrow_up_small { background-position: -1000px 0px; }
43 | .emoji.s_arrow_up { background-position: -1025px 0px; }
44 | .emoji.s_arrow_upper_left { background-position: -1050px 0px; }
45 | .emoji.s_arrow_upper_right { background-position: -1075px 0px; }
46 | .emoji.s_arrows_clockwise { background-position: -1100px 0px; }
47 | .emoji.s_arrows_counterclockwise { background-position: -1125px 0px; }
48 | .emoji.s_art { background-position: -1150px 0px; }
49 | .emoji.s_articulated_lorry { background-position: -1175px 0px; }
50 | .emoji.s_astonished { background-position: -1200px 0px; }
51 | .emoji.s_athletic_shoe { background-position: -1225px 0px; }
52 | .emoji.s_atm { background-position: -1250px 0px; }
53 | .emoji.s_b { background-position: -1275px 0px; }
54 | .emoji.s_baby_bottle { background-position: -1300px 0px; }
55 | .emoji.s_baby_chick { background-position: -1325px 0px; }
56 | .emoji.s_baby_symbol { background-position: -1350px 0px; }
57 | .emoji.s_baby { background-position: -1375px 0px; }
58 | .emoji.s_back { background-position: -1400px 0px; }
59 | .emoji.s_baggage_claim { background-position: -1425px 0px; }
60 | .emoji.s_balloon { background-position: -1450px 0px; }
61 | .emoji.s_ballot_box_with_check { background-position: -1475px 0px; }
62 | .emoji.s_bamboo { background-position: -1500px 0px; }
63 | .emoji.s_banana { background-position: -1525px 0px; }
64 | .emoji.s_bangbang { background-position: -1550px 0px; }
65 | .emoji.s_bank { background-position: -1575px 0px; }
66 | .emoji.s_bar_chart { background-position: -1600px 0px; }
67 | .emoji.s_barber { background-position: -1625px 0px; }
68 | .emoji.s_baseball { background-position: -1650px 0px; }
69 | .emoji.s_basketball { background-position: -1675px 0px; }
70 | .emoji.s_bath { background-position: -1700px 0px; }
71 | .emoji.s_bathtub { background-position: -1725px 0px; }
72 | .emoji.s_battery { background-position: -1750px 0px; }
73 | .emoji.s_bear { background-position: -1775px 0px; }
74 | .emoji.s_honeybee { background-position: -1800px 0px; }
75 | .emoji.s_bee { background-position: -1825px 0px; }
76 | .emoji.s_beer { background-position: -1850px 0px; }
77 | .emoji.s_beers { background-position: -1875px 0px; }
78 | .emoji.s_beetle { background-position: -1900px 0px; }
79 | .emoji.s_beginner { background-position: -1925px 0px; }
80 | .emoji.s_bell { background-position: -1950px 0px; }
81 | .emoji.s_bento { background-position: -1975px 0px; }
82 | .emoji.s_bicyclist { background-position: -2000px 0px; }
83 | .emoji.s_bike { background-position: -2025px 0px; }
84 | .emoji.s_bikini { background-position: -2050px 0px; }
85 | .emoji.s_bird { background-position: -2075px 0px; }
86 | .emoji.s_birthday { background-position: -2100px 0px; }
87 | .emoji.s_black_circle { background-position: -2125px 0px; }
88 | .emoji.s_black_joker { background-position: -2150px 0px; }
89 | .emoji.s_black_large_square { background-position: -2175px 0px; }
90 | .emoji.s_black_medium_small_square { background-position: -2200px 0px; }
91 | .emoji.s_black_medium_square { background-position: -2225px 0px; }
92 | .emoji.s_black_nib { background-position: -2250px 0px; }
93 | .emoji.s_black_small_square { background-position: -2275px 0px; }
94 | .emoji.s_black_square_button { background-position: -2300px 0px; }
95 | .emoji.s_blossom { background-position: -2325px 0px; }
96 | .emoji.s_blowfish { background-position: -2350px 0px; }
97 | .emoji.s_blue_book { background-position: -2375px 0px; }
98 | .emoji.s_blue_car { background-position: -2400px 0px; }
99 | .emoji.s_blue_heart { background-position: -2425px 0px; }
100 | .emoji.s_blush { background-position: -2450px 0px; }
101 | .emoji.s_boar { background-position: -2475px 0px; }
102 | .emoji.s_sailboat { background-position: -2500px 0px; }
103 | .emoji.s_boat { background-position: -2525px 0px; }
104 | .emoji.s_bomb { background-position: -2550px 0px; }
105 | .emoji.s_book { background-position: -2575px 0px; }
106 | .emoji.s_open_book { background-position: -2600px 0px; }
107 | .emoji.s_bookmark_tabs { background-position: -2625px 0px; }
108 | .emoji.s_bookmark { background-position: -2650px 0px; }
109 | .emoji.s_books { background-position: -2675px 0px; }
110 | .emoji.s_boom { background-position: -2700px 0px; }
111 | .emoji.s_collision { background-position: -2725px 0px; }
112 | .emoji.s_boot { background-position: -2750px 0px; }
113 | .emoji.s_bouquet { background-position: -2775px 0px; }
114 | .emoji.s_bow { background-position: -2800px 0px; }
115 | .emoji.s_bowling { background-position: -2825px 0px; }
116 | .emoji.s_bowtie { background-position: -2850px 0px; }
117 | .emoji.s_boy { background-position: -2875px 0px; }
118 | .emoji.s_bread { background-position: -2900px 0px; }
119 | .emoji.s_bride_with_veil { background-position: -2925px 0px; }
120 | .emoji.s_bridge_at_night { background-position: -2950px 0px; }
121 | .emoji.s_briefcase { background-position: -2975px 0px; }
122 | .emoji.s_broken_heart { background-position: -3000px 0px; }
123 | .emoji.s_bug { background-position: -3025px 0px; }
124 | .emoji.s_bulb { background-position: -3050px 0px; }
125 | .emoji.s_bullettrain_front { background-position: -3075px 0px; }
126 | .emoji.s_bullettrain_side { background-position: -3100px 0px; }
127 | .emoji.s_bus { background-position: -3125px 0px; }
128 | .emoji.s_busstop { background-position: -3150px 0px; }
129 | .emoji.s_bust_in_silhouette { background-position: -3175px 0px; }
130 | .emoji.s_busts_in_silhouette { background-position: -3200px 0px; }
131 | .emoji.s_cactus { background-position: -3225px 0px; }
132 | .emoji.s_cake { background-position: -3250px 0px; }
133 | .emoji.s_calendar { background-position: -3275px 0px; }
134 | .emoji.s_calling { background-position: -3300px 0px; }
135 | .emoji.s_camel { background-position: -3325px 0px; }
136 | .emoji.s_camera { background-position: -3350px 0px; }
137 | .emoji.s_cancer { background-position: -3375px 0px; }
138 | .emoji.s_candy { background-position: -3400px 0px; }
139 | .emoji.s_capital_abcd { background-position: -3425px 0px; }
140 | .emoji.s_capricorn { background-position: -3450px 0px; }
141 | .emoji.s_red_car { background-position: -3475px 0px; }
142 | .emoji.s_car { background-position: -3500px 0px; }
143 | .emoji.s_card_index { background-position: -3525px 0px; }
144 | .emoji.s_carousel_horse { background-position: -3550px 0px; }
145 | .emoji.s_cat { background-position: -3575px 0px; }
146 | .emoji.s_cat2 { background-position: -3600px 0px; }
147 | .emoji.s_cd { background-position: -3625px 0px; }
148 | .emoji.s_chart_with_downwards_trend { background-position: -3650px 0px; }
149 | .emoji.s_chart_with_upwards_trend { background-position: -3675px 0px; }
150 | .emoji.s_chart { background-position: -3700px 0px; }
151 | .emoji.s_checkered_flag { background-position: -3725px 0px; }
152 | .emoji.s_cherries { background-position: -3750px 0px; }
153 | .emoji.s_cherry_blossom { background-position: -3775px 0px; }
154 | .emoji.s_chestnut { background-position: -3800px 0px; }
155 | .emoji.s_chicken { background-position: -3825px 0px; }
156 | .emoji.s_children_crossing { background-position: -3850px 0px; }
157 | .emoji.s_chocolate_bar { background-position: -3875px 0px; }
158 | .emoji.s_christmas_tree { background-position: -3900px 0px; }
159 | .emoji.s_church { background-position: -3925px 0px; }
160 | .emoji.s_cinema { background-position: -3950px 0px; }
161 | .emoji.s_circus_tent { background-position: -3975px 0px; }
162 | .emoji.s_city_sunrise { background-position: -4000px 0px; }
163 | .emoji.s_city_sunset { background-position: -4025px 0px; }
164 | .emoji.s_cl { background-position: -4050px 0px; }
165 | .emoji.s_clap { background-position: -4075px 0px; }
166 | .emoji.s_clapper { background-position: -4100px 0px; }
167 | .emoji.s_clipboard { background-position: -4125px 0px; }
168 | .emoji.s_clock1 { background-position: -4150px 0px; }
169 | .emoji.s_clock2 { background-position: -4175px 0px; }
170 | .emoji.s_clock3 { background-position: -4200px 0px; }
171 | .emoji.s_clock4 { background-position: -4225px 0px; }
172 | .emoji.s_clock5 { background-position: -4250px 0px; }
173 | .emoji.s_clock6 { background-position: -4275px 0px; }
174 | .emoji.s_clock7 { background-position: -4300px 0px; }
175 | .emoji.s_clock8 { background-position: -4325px 0px; }
176 | .emoji.s_clock9 { background-position: -4350px 0px; }
177 | .emoji.s_clock10 { background-position: -4375px 0px; }
178 | .emoji.s_clock11 { background-position: -4400px 0px; }
179 | .emoji.s_clock12 { background-position: -4425px 0px; }
180 | .emoji.s_clock130 { background-position: -4450px 0px; }
181 | .emoji.s_clock230 { background-position: -4475px 0px; }
182 | .emoji.s_clock330 { background-position: -4500px 0px; }
183 | .emoji.s_clock430 { background-position: -4525px 0px; }
184 | .emoji.s_clock530 { background-position: -4550px 0px; }
185 | .emoji.s_clock630 { background-position: -4575px 0px; }
186 | .emoji.s_clock730 { background-position: -4600px 0px; }
187 | .emoji.s_clock830 { background-position: -4625px 0px; }
188 | .emoji.s_clock930 { background-position: -4650px 0px; }
189 | .emoji.s_clock1030 { background-position: -4675px 0px; }
190 | .emoji.s_clock1130 { background-position: -4700px 0px; }
191 | .emoji.s_clock1230 { background-position: -4725px 0px; }
192 | .emoji.s_closed_book { background-position: -4750px 0px; }
193 | .emoji.s_closed_lock_with_key { background-position: -4775px 0px; }
194 | .emoji.s_closed_umbrella { background-position: -4800px 0px; }
195 | .emoji.s_cloud { background-position: -4825px 0px; }
196 | .emoji.s_clubs { background-position: -4850px 0px; }
197 | .emoji.s_cn { background-position: -4875px 0px; }
198 | .emoji.s_cocktail { background-position: -4900px 0px; }
199 | .emoji.s_coffee { background-position: -4925px 0px; }
200 | .emoji.s_cold_sweat { background-position: -4950px 0px; }
201 | .emoji.s_computer { background-position: -4975px 0px; }
202 | .emoji.s_confetti_ball { background-position: -5000px 0px; }
203 | .emoji.s_confounded { background-position: -5025px 0px; }
204 | .emoji.s_confused { background-position: -5050px 0px; }
205 | .emoji.s_congratulations { background-position: -5075px 0px; }
206 | .emoji.s_construction_worker { background-position: -5100px 0px; }
207 | .emoji.s_construction { background-position: -5125px 0px; }
208 | .emoji.s_convenience_store { background-position: -5150px 0px; }
209 | .emoji.s_cookie { background-position: -5175px 0px; }
210 | .emoji.s_cool { background-position: -5200px 0px; }
211 | .emoji.s_cop { background-position: -5225px 0px; }
212 | .emoji.s_copyright { background-position: -5250px 0px; }
213 | .emoji.s_corn { background-position: -5275px 0px; }
214 | .emoji.s_couple_with_heart { background-position: -5300px 0px; }
215 | .emoji.s_couple { background-position: -5325px 0px; }
216 | .emoji.s_couplekiss { background-position: -5350px 0px; }
217 | .emoji.s_cow { background-position: -5375px 0px; }
218 | .emoji.s_cow2 { background-position: -5400px 0px; }
219 | .emoji.s_credit_card { background-position: -5425px 0px; }
220 | .emoji.s_crescent_moon { background-position: -5450px 0px; }
221 | .emoji.s_crocodile { background-position: -5475px 0px; }
222 | .emoji.s_crossed_flags { background-position: -5500px 0px; }
223 | .emoji.s_crown { background-position: -5525px 0px; }
224 | .emoji.s_cry { background-position: -5550px 0px; }
225 | .emoji.s_crying_cat_face { background-position: -5575px 0px; }
226 | .emoji.s_crystal_ball { background-position: -5600px 0px; }
227 | .emoji.s_cupid { background-position: -5625px 0px; }
228 | .emoji.s_curly_loop { background-position: -5650px 0px; }
229 | .emoji.s_currency_exchange { background-position: -5675px 0px; }
230 | .emoji.s_curry { background-position: -5700px 0px; }
231 | .emoji.s_custard { background-position: -5725px 0px; }
232 | .emoji.s_customs { background-position: -5750px 0px; }
233 | .emoji.s_cyclone { background-position: -5775px 0px; }
234 | .emoji.s_dancer { background-position: -5800px 0px; }
235 | .emoji.s_dancers { background-position: -5825px 0px; }
236 | .emoji.s_dango { background-position: -5850px 0px; }
237 | .emoji.s_dart { background-position: -5875px 0px; }
238 | .emoji.s_dash { background-position: -5900px 0px; }
239 | .emoji.s_date { background-position: -5925px 0px; }
240 | .emoji.s_de { background-position: -5950px 0px; }
241 | .emoji.s_deciduous_tree { background-position: -5975px 0px; }
242 | .emoji.s_department_store { background-position: -6000px 0px; }
243 | .emoji.s_diamond_shape_with_a_dot_inside { background-position: -6025px 0px; }
244 | .emoji.s_diamonds { background-position: -6050px 0px; }
245 | .emoji.s_disappointed_relieved { background-position: -6075px 0px; }
246 | .emoji.s_disappointed { background-position: -6100px 0px; }
247 | .emoji.s_dizzy_face { background-position: -6125px 0px; }
248 | .emoji.s_dizzy { background-position: -6150px 0px; }
249 | .emoji.s_do_not_litter { background-position: -6175px 0px; }
250 | .emoji.s_dog { background-position: -6200px 0px; }
251 | .emoji.s_dog2 { background-position: -6225px 0px; }
252 | .emoji.s_dollar { background-position: -6250px 0px; }
253 | .emoji.s_dolls { background-position: -6275px 0px; }
254 | .emoji.s_dolphin { background-position: -6300px 0px; }
255 | .emoji.s_door { background-position: -6325px 0px; }
256 | .emoji.s_doughnut { background-position: -6350px 0px; }
257 | .emoji.s_dragon_face { background-position: -6375px 0px; }
258 | .emoji.s_dragon { background-position: -6400px 0px; }
259 | .emoji.s_dress { background-position: -6425px 0px; }
260 | .emoji.s_dromedary_camel { background-position: -6450px 0px; }
261 | .emoji.s_droplet { background-position: -6475px 0px; }
262 | .emoji.s_dvd { background-position: -6500px 0px; }
263 | .emoji.s_e-mail { background-position: -6525px 0px; }
264 | .emoji.s_ear_of_rice { background-position: -6550px 0px; }
265 | .emoji.s_ear { background-position: -6575px 0px; }
266 | .emoji.s_earth_africa { background-position: -6600px 0px; }
267 | .emoji.s_earth_americas { background-position: -6625px 0px; }
268 | .emoji.s_earth_asia { background-position: -6650px 0px; }
269 | .emoji.s_egg { background-position: -6675px 0px; }
270 | .emoji.s_eggplant { background-position: -6700px 0px; }
271 | .emoji.s_eight_pointed_black_star { background-position: -6725px 0px; }
272 | .emoji.s_eight_spoked_asterisk { background-position: -6750px 0px; }
273 | .emoji.s_eight { background-position: -6775px 0px; }
274 | .emoji.s_electric_plug { background-position: -6800px 0px; }
275 | .emoji.s_elephant { background-position: -6825px 0px; }
276 | .emoji.s_envelope { background-position: -6850px 0px; }
277 | .emoji.s_email { background-position: -6875px 0px; }
278 | .emoji.s_end { background-position: -6900px 0px; }
279 | .emoji.s_envelope_with_arrow { background-position: -6925px 0px; }
280 | .emoji.s_es { background-position: -6950px 0px; }
281 | .emoji.s_euro { background-position: -6975px 0px; }
282 | .emoji.s_european_castle { background-position: -7000px 0px; }
283 | .emoji.s_european_post_office { background-position: -7025px 0px; }
284 | .emoji.s_evergreen_tree { background-position: -7050px 0px; }
285 | .emoji.s_exclamation { background-position: -7075px 0px; }
286 | .emoji.s_heavy_exclamation_mark { background-position: -7100px 0px; }
287 | .emoji.s_expressionless { background-position: -7125px 0px; }
288 | .emoji.s_eyeglasses { background-position: -7150px 0px; }
289 | .emoji.s_eyes { background-position: -7175px 0px; }
290 | .emoji.s_punch { background-position: -7200px 0px; }
291 | .emoji.s_facepunch { background-position: -7225px 0px; }
292 | .emoji.s_factory { background-position: -7250px 0px; }
293 | .emoji.s_fallen_leaf { background-position: -7275px 0px; }
294 | .emoji.s_family { background-position: -7300px 0px; }
295 | .emoji.s_fast_forward { background-position: -7325px 0px; }
296 | .emoji.s_fax { background-position: -7350px 0px; }
297 | .emoji.s_fearful { background-position: -7375px 0px; }
298 | .emoji.s_feelsgood { background-position: -7400px 0px; }
299 | .emoji.s_paw_prints { background-position: -7425px 0px; }
300 | .emoji.s_feet { background-position: -7450px 0px; }
301 | .emoji.s_ferris_wheel { background-position: -7475px 0px; }
302 | .emoji.s_file_folder { background-position: -7500px 0px; }
303 | .emoji.s_finnadie { background-position: -7525px 0px; }
304 | .emoji.s_fire_engine { background-position: -7550px 0px; }
305 | .emoji.s_fire { background-position: -7575px 0px; }
306 | .emoji.s_fireworks { background-position: -7600px 0px; }
307 | .emoji.s_first_quarter_moon_with_face { background-position: -7625px 0px; }
308 | .emoji.s_first_quarter_moon { background-position: -7650px 0px; }
309 | .emoji.s_fish_cake { background-position: -7675px 0px; }
310 | .emoji.s_fish { background-position: -7700px 0px; }
311 | .emoji.s_fishing_pole_and_fish { background-position: -7725px 0px; }
312 | .emoji.s_fist { background-position: -7750px 0px; }
313 | .emoji.s_five { background-position: -7775px 0px; }
314 | .emoji.s_flags { background-position: -7800px 0px; }
315 | .emoji.s_flashlight { background-position: -7825px 0px; }
316 | .emoji.s_floppy_disk { background-position: -7850px 0px; }
317 | .emoji.s_flower_playing_cards { background-position: -7875px 0px; }
318 | .emoji.s_flushed { background-position: -7900px 0px; }
319 | .emoji.s_foggy { background-position: -7925px 0px; }
320 | .emoji.s_football { background-position: -7950px 0px; }
321 | .emoji.s_footprints { background-position: -7975px 0px; }
322 | .emoji.s_fork_and_knife { background-position: -8000px 0px; }
323 | .emoji.s_fountain { background-position: -8025px 0px; }
324 | .emoji.s_four_leaf_clover { background-position: -8050px 0px; }
325 | .emoji.s_four { background-position: -8075px 0px; }
326 | .emoji.s_fr { background-position: -8100px 0px; }
327 | .emoji.s_free { background-position: -8125px 0px; }
328 | .emoji.s_fried_shrimp { background-position: -8150px 0px; }
329 | .emoji.s_fries { background-position: -8175px 0px; }
330 | .emoji.s_frog { background-position: -8200px 0px; }
331 | .emoji.s_frowning { background-position: -8225px 0px; }
332 | .emoji.s_fuelpump { background-position: -8250px 0px; }
333 | .emoji.s_full_moon_with_face { background-position: -8275px 0px; }
334 | .emoji.s_full_moon { background-position: -8300px 0px; }
335 | .emoji.s_game_die { background-position: -8325px 0px; }
336 | .emoji.s_gb { background-position: -8350px 0px; }
337 | .emoji.s_uk { background-position: -8375px 0px; }
338 | .emoji.s_gem { background-position: -8400px 0px; }
339 | .emoji.s_gemini { background-position: -8425px 0px; }
340 | .emoji.s_ghost { background-position: -8450px 0px; }
341 | .emoji.s_gift_heart { background-position: -8475px 0px; }
342 | .emoji.s_gift { background-position: -8500px 0px; }
343 | .emoji.s_girl { background-position: -8525px 0px; }
344 | .emoji.s_globe_with_meridians { background-position: -8550px 0px; }
345 | .emoji.s_goat { background-position: -8575px 0px; }
346 | .emoji.s_goberserk { background-position: -8600px 0px; }
347 | .emoji.s_godmode { background-position: -8625px 0px; }
348 | .emoji.s_golf { background-position: -8650px 0px; }
349 | .emoji.s_grapes { background-position: -8675px 0px; }
350 | .emoji.s_green_apple { background-position: -8700px 0px; }
351 | .emoji.s_green_book { background-position: -8725px 0px; }
352 | .emoji.s_green_heart { background-position: -8750px 0px; }
353 | .emoji.s_grey_exclamation { background-position: -8775px 0px; }
354 | .emoji.s_grey_question { background-position: -8800px 0px; }
355 | .emoji.s_grimacing { background-position: -8825px 0px; }
356 | .emoji.s_grin { background-position: -8850px 0px; }
357 | .emoji.s_grinning { background-position: -8875px 0px; }
358 | .emoji.s_guardsman { background-position: -8900px 0px; }
359 | .emoji.s_guitar { background-position: -8925px 0px; }
360 | .emoji.s_gun { background-position: -8950px 0px; }
361 | .emoji.s_haircut { background-position: -8975px 0px; }
362 | .emoji.s_hamburger { background-position: -9000px 0px; }
363 | .emoji.s_hammer { background-position: -9025px 0px; }
364 | .emoji.s_hamster { background-position: -9050px 0px; }
365 | .emoji.s_hand { background-position: -9075px 0px; }
366 | .emoji.s_raised_hand { background-position: -9100px 0px; }
367 | .emoji.s_handbag { background-position: -9125px 0px; }
368 | .emoji.s_poop { background-position: -9150px 0px; }
369 | .emoji.s_hankey { background-position: -9175px 0px; }
370 | .emoji.s_shit { background-position: -9200px 0px; }
371 | .emoji.s_hash { background-position: -9225px 0px; }
372 | .emoji.s_hatched_chick { background-position: -9250px 0px; }
373 | .emoji.s_hatching_chick { background-position: -9275px 0px; }
374 | .emoji.s_headphones { background-position: -9300px 0px; }
375 | .emoji.s_hear_no_evil { background-position: -9325px 0px; }
376 | .emoji.s_heart_decoration { background-position: -9350px 0px; }
377 | .emoji.s_heart_eyes_cat { background-position: -9375px 0px; }
378 | .emoji.s_heart_eyes { background-position: -9400px 0px; }
379 | .emoji.s_heart { background-position: -9425px 0px; }
380 | .emoji.s_heartbeat { background-position: -9450px 0px; }
381 | .emoji.s_heartpulse { background-position: -9475px 0px; }
382 | .emoji.s_hearts { background-position: -9500px 0px; }
383 | .emoji.s_heavy_check_mark { background-position: -9525px 0px; }
384 | .emoji.s_heavy_division_sign { background-position: -9550px 0px; }
385 | .emoji.s_heavy_dollar_sign { background-position: -9575px 0px; }
386 | .emoji.s_heavy_minus_sign { background-position: -9600px 0px; }
387 | .emoji.s_heavy_multiplication_x { background-position: -9625px 0px; }
388 | .emoji.s_heavy_plus_sign { background-position: -9650px 0px; }
389 | .emoji.s_helicopter { background-position: -9675px 0px; }
390 | .emoji.s_herb { background-position: -9700px 0px; }
391 | .emoji.s_hibiscus { background-position: -9725px 0px; }
392 | .emoji.s_high_brightness { background-position: -9750px 0px; }
393 | .emoji.s_high_heel { background-position: -9775px 0px; }
394 | .emoji.s_hocho { background-position: -9800px 0px; }
395 | .emoji.s_honey_pot { background-position: -9825px 0px; }
396 | .emoji.s_horse_racing { background-position: -9850px 0px; }
397 | .emoji.s_horse { background-position: -9875px 0px; }
398 | .emoji.s_hospital { background-position: -9900px 0px; }
399 | .emoji.s_hotel { background-position: -9925px 0px; }
400 | .emoji.s_hotsprings { background-position: -9950px 0px; }
401 | .emoji.s_hourglass_flowing_sand { background-position: -9975px 0px; }
402 | .emoji.s_hourglass { background-position: -10000px 0px; }
403 | .emoji.s_house_with_garden { background-position: -10025px 0px; }
404 | .emoji.s_house { background-position: -10050px 0px; }
405 | .emoji.s_hurtrealbad { background-position: -10075px 0px; }
406 | .emoji.s_hushed { background-position: -10100px 0px; }
407 | .emoji.s_ice_cream { background-position: -10125px 0px; }
408 | .emoji.s_icecream { background-position: -10150px 0px; }
409 | .emoji.s_id { background-position: -10175px 0px; }
410 | .emoji.s_ideograph_advantage { background-position: -10200px 0px; }
411 | .emoji.s_imp { background-position: -10225px 0px; }
412 | .emoji.s_inbox_tray { background-position: -10250px 0px; }
413 | .emoji.s_incoming_envelope { background-position: -10275px 0px; }
414 | .emoji.s_information_desk_person { background-position: -10300px 0px; }
415 | .emoji.s_information_source { background-position: -10325px 0px; }
416 | .emoji.s_innocent { background-position: -10350px 0px; }
417 | .emoji.s_interrobang { background-position: -10375px 0px; }
418 | .emoji.s_iphone { background-position: -10400px 0px; }
419 | .emoji.s_it { background-position: -10425px 0px; }
420 | .emoji.s_lantern { background-position: -10450px 0px; }
421 | .emoji.s_izakaya_lantern { background-position: -10475px 0px; }
422 | .emoji.s_jack_o_lantern { background-position: -10500px 0px; }
423 | .emoji.s_japan { background-position: -10525px 0px; }
424 | .emoji.s_japanese_castle { background-position: -10550px 0px; }
425 | .emoji.s_japanese_goblin { background-position: -10575px 0px; }
426 | .emoji.s_japanese_ogre { background-position: -10600px 0px; }
427 | .emoji.s_jeans { background-position: -10625px 0px; }
428 | .emoji.s_joy_cat { background-position: -10650px 0px; }
429 | .emoji.s_joy { background-position: -10675px 0px; }
430 | .emoji.s_jp { background-position: -10700px 0px; }
431 | .emoji.s_key { background-position: -10725px 0px; }
432 | .emoji.s_keycap_ten { background-position: -10750px 0px; }
433 | .emoji.s_kimono { background-position: -10775px 0px; }
434 | .emoji.s_kiss { background-position: -10800px 0px; }
435 | .emoji.s_kissing_cat { background-position: -10825px 0px; }
436 | .emoji.s_kissing_closed_eyes { background-position: -10850px 0px; }
437 | .emoji.s_kissing_heart { background-position: -10875px 0px; }
438 | .emoji.s_kissing_smiling_eyes { background-position: -10900px 0px; }
439 | .emoji.s_kissing { background-position: -10925px 0px; }
440 | .emoji.s_koala { background-position: -10950px 0px; }
441 | .emoji.s_koko { background-position: -10975px 0px; }
442 | .emoji.s_kr { background-position: -11000px 0px; }
443 | .emoji.s_large_blue_circle { background-position: -11025px 0px; }
444 | .emoji.s_large_blue_diamond { background-position: -11050px 0px; }
445 | .emoji.s_large_orange_diamond { background-position: -11075px 0px; }
446 | .emoji.s_last_quarter_moon_with_face { background-position: -11100px 0px; }
447 | .emoji.s_last_quarter_moon { background-position: -11125px 0px; }
448 | .emoji.s_satisfied { background-position: -11150px 0px; }
449 | .emoji.s_laughing { background-position: -11175px 0px; }
450 | .emoji.s_leaves { background-position: -11200px 0px; }
451 | .emoji.s_ledger { background-position: -11225px 0px; }
452 | .emoji.s_left_luggage { background-position: -11250px 0px; }
453 | .emoji.s_left_right_arrow { background-position: -11275px 0px; }
454 | .emoji.s_leftwards_arrow_with_hook { background-position: -11300px 0px; }
455 | .emoji.s_lemon { background-position: -11325px 0px; }
456 | .emoji.s_leo { background-position: -11350px 0px; }
457 | .emoji.s_leopard { background-position: -11375px 0px; }
458 | .emoji.s_libra { background-position: -11400px 0px; }
459 | .emoji.s_light_rail { background-position: -11425px 0px; }
460 | .emoji.s_link { background-position: -11450px 0px; }
461 | .emoji.s_lips { background-position: -11475px 0px; }
462 | .emoji.s_lipstick { background-position: -11500px 0px; }
463 | .emoji.s_lock_with_ink_pen { background-position: -11525px 0px; }
464 | .emoji.s_lock { background-position: -11550px 0px; }
465 | .emoji.s_lollipop { background-position: -11575px 0px; }
466 | .emoji.s_loop { background-position: -11600px 0px; }
467 | .emoji.s_loudspeaker { background-position: -11625px 0px; }
468 | .emoji.s_love_hotel { background-position: -11650px 0px; }
469 | .emoji.s_love_letter { background-position: -11675px 0px; }
470 | .emoji.s_low_brightness { background-position: -11700px 0px; }
471 | .emoji.s_m { background-position: -11725px 0px; }
472 | .emoji.s_mag_right { background-position: -11750px 0px; }
473 | .emoji.s_mag { background-position: -11775px 0px; }
474 | .emoji.s_mahjong { background-position: -11800px 0px; }
475 | .emoji.s_mailbox_closed { background-position: -11825px 0px; }
476 | .emoji.s_mailbox_with_mail { background-position: -11850px 0px; }
477 | .emoji.s_mailbox_with_no_mail { background-position: -11875px 0px; }
478 | .emoji.s_mailbox { background-position: -11900px 0px; }
479 | .emoji.s_man_with_gua_pi_mao { background-position: -11925px 0px; }
480 | .emoji.s_man_with_turban { background-position: -11950px 0px; }
481 | .emoji.s_man { background-position: -11975px 0px; }
482 | .emoji.s_mans_shoe { background-position: -12000px 0px; }
483 | .emoji.s_shoe { background-position: -12025px 0px; }
484 | .emoji.s_maple_leaf { background-position: -12050px 0px; }
485 | .emoji.s_mask { background-position: -12075px 0px; }
486 | .emoji.s_massage { background-position: -12100px 0px; }
487 | .emoji.s_meat_on_bone { background-position: -12125px 0px; }
488 | .emoji.s_mega { background-position: -12150px 0px; }
489 | .emoji.s_melon { background-position: -12175px 0px; }
490 | .emoji.s_memo { background-position: -12200px 0px; }
491 | .emoji.s_pencil { background-position: -12225px 0px; }
492 | .emoji.s_mens { background-position: -12250px 0px; }
493 | .emoji.s_metal { background-position: -12275px 0px; }
494 | .emoji.s_metro { background-position: -12300px 0px; }
495 | .emoji.s_microphone { background-position: -12325px 0px; }
496 | .emoji.s_microscope { background-position: -12350px 0px; }
497 | .emoji.s_milky_way { background-position: -12375px 0px; }
498 | .emoji.s_minibus { background-position: -12400px 0px; }
499 | .emoji.s_minidisc { background-position: -12425px 0px; }
500 | .emoji.s_mobile_phone_off { background-position: -12450px 0px; }
501 | .emoji.s_money_with_wings { background-position: -12475px 0px; }
502 | .emoji.s_moneybag { background-position: -12500px 0px; }
503 | .emoji.s_monkey_face { background-position: -12525px 0px; }
504 | .emoji.s_monkey { background-position: -12550px 0px; }
505 | .emoji.s_monorail { background-position: -12575px 0px; }
506 | .emoji.s_moon { background-position: -12600px 0px; }
507 | .emoji.s_waxing_gibbous_moon { background-position: -12625px 0px; }
508 | .emoji.s_mortar_board { background-position: -12650px 0px; }
509 | .emoji.s_mount_fuji { background-position: -12675px 0px; }
510 | .emoji.s_mountain_bicyclist { background-position: -12700px 0px; }
511 | .emoji.s_mountain_cableway { background-position: -12725px 0px; }
512 | .emoji.s_mountain_railway { background-position: -12750px 0px; }
513 | .emoji.s_mouse { background-position: -12775px 0px; }
514 | .emoji.s_mouse2 { background-position: -12800px 0px; }
515 | .emoji.s_movie_camera { background-position: -12825px 0px; }
516 | .emoji.s_moyai { background-position: -12850px 0px; }
517 | .emoji.s_muscle { background-position: -12875px 0px; }
518 | .emoji.s_mushroom { background-position: -12900px 0px; }
519 | .emoji.s_musical_keyboard { background-position: -12925px 0px; }
520 | .emoji.s_musical_note { background-position: -12950px 0px; }
521 | .emoji.s_musical_score { background-position: -12975px 0px; }
522 | .emoji.s_mute { background-position: -13000px 0px; }
523 | .emoji.s_nail_care { background-position: -13025px 0px; }
524 | .emoji.s_name_badge { background-position: -13050px 0px; }
525 | .emoji.s_neckbeard { background-position: -13075px 0px; }
526 | .emoji.s_necktie { background-position: -13100px 0px; }
527 | .emoji.s_negative_squared_cross_mark { background-position: -13125px 0px; }
528 | .emoji.s_neutral_face { background-position: -13150px 0px; }
529 | .emoji.s_new_moon_with_face { background-position: -13175px 0px; }
530 | .emoji.s_new_moon { background-position: -13200px 0px; }
531 | .emoji.s_new { background-position: -13225px 0px; }
532 | .emoji.s_newspaper { background-position: -13250px 0px; }
533 | .emoji.s_ng { background-position: -13275px 0px; }
534 | .emoji.s_nine { background-position: -13300px 0px; }
535 | .emoji.s_no_bell { background-position: -13325px 0px; }
536 | .emoji.s_no_bicycles { background-position: -13350px 0px; }
537 | .emoji.s_no_entry_sign { background-position: -13375px 0px; }
538 | .emoji.s_no_entry { background-position: -13400px 0px; }
539 | .emoji.s_no_good { background-position: -13425px 0px; }
540 | .emoji.s_no_mobile_phones { background-position: -13450px 0px; }
541 | .emoji.s_no_mouth { background-position: -13475px 0px; }
542 | .emoji.s_no_pedestrians { background-position: -13500px 0px; }
543 | .emoji.s_no_smoking { background-position: -13525px 0px; }
544 | .emoji.s_non-potable_water { background-position: -13550px 0px; }
545 | .emoji.s_nose { background-position: -13575px 0px; }
546 | .emoji.s_notebook_with_decorative_cover { background-position: -13600px 0px; }
547 | .emoji.s_notebook { background-position: -13625px 0px; }
548 | .emoji.s_notes { background-position: -13650px 0px; }
549 | .emoji.s_nut_and_bolt { background-position: -13675px 0px; }
550 | .emoji.s_o { background-position: -13700px 0px; }
551 | .emoji.s_o2 { background-position: -13725px 0px; }
552 | .emoji.s_ocean { background-position: -13750px 0px; }
553 | .emoji.s_octocat { background-position: -13775px 0px; }
554 | .emoji.s_octopus { background-position: -13800px 0px; }
555 | .emoji.s_oden { background-position: -13825px 0px; }
556 | .emoji.s_office { background-position: -13850px 0px; }
557 | .emoji.s_ok_hand { background-position: -13875px 0px; }
558 | .emoji.s_ok_woman { background-position: -13900px 0px; }
559 | .emoji.s_ok { background-position: -13925px 0px; }
560 | .emoji.s_older_man { background-position: -13950px 0px; }
561 | .emoji.s_older_woman { background-position: -13975px 0px; }
562 | .emoji.s_on { background-position: -14000px 0px; }
563 | .emoji.s_oncoming_automobile { background-position: -14025px 0px; }
564 | .emoji.s_oncoming_bus { background-position: -14050px 0px; }
565 | .emoji.s_oncoming_police_car { background-position: -14075px 0px; }
566 | .emoji.s_oncoming_taxi { background-position: -14100px 0px; }
567 | .emoji.s_one { background-position: -14125px 0px; }
568 | .emoji.s_open_file_folder { background-position: -14150px 0px; }
569 | .emoji.s_open_hands { background-position: -14175px 0px; }
570 | .emoji.s_open_mouth { background-position: -14200px 0px; }
571 | .emoji.s_ophiuchus { background-position: -14225px 0px; }
572 | .emoji.s_orange_book { background-position: -14250px 0px; }
573 | .emoji.s_outbox_tray { background-position: -14275px 0px; }
574 | .emoji.s_ox { background-position: -14300px 0px; }
575 | .emoji.s_package { background-position: -14325px 0px; }
576 | .emoji.s_page_facing_up { background-position: -14350px 0px; }
577 | .emoji.s_page_with_curl { background-position: -14375px 0px; }
578 | .emoji.s_pager { background-position: -14400px 0px; }
579 | .emoji.s_palm_tree { background-position: -14425px 0px; }
580 | .emoji.s_panda_face { background-position: -14450px 0px; }
581 | .emoji.s_paperclip { background-position: -14475px 0px; }
582 | .emoji.s_parking { background-position: -14500px 0px; }
583 | .emoji.s_part_alternation_mark { background-position: -14525px 0px; }
584 | .emoji.s_partly_sunny { background-position: -14550px 0px; }
585 | .emoji.s_passport_control { background-position: -14575px 0px; }
586 | .emoji.s_peach { background-position: -14600px 0px; }
587 | .emoji.s_pear { background-position: -14625px 0px; }
588 | .emoji.s_pencil2 { background-position: -14650px 0px; }
589 | .emoji.s_penguin { background-position: -14675px 0px; }
590 | .emoji.s_pensive { background-position: -14700px 0px; }
591 | .emoji.s_performing_arts { background-position: -14725px 0px; }
592 | .emoji.s_persevere { background-position: -14750px 0px; }
593 | .emoji.s_person_frowning { background-position: -14775px 0px; }
594 | .emoji.s_person_with_blond_hair { background-position: -14800px 0px; }
595 | .emoji.s_person_with_pouting_face { background-position: -14825px 0px; }
596 | .emoji.s_telephone { background-position: -14850px 0px; }
597 | .emoji.s_phone { background-position: -14875px 0px; }
598 | .emoji.s_pig_nose { background-position: -14900px 0px; }
599 | .emoji.s_pig { background-position: -14925px 0px; }
600 | .emoji.s_pig2 { background-position: -14950px 0px; }
601 | .emoji.s_pill { background-position: -14975px 0px; }
602 | .emoji.s_pineapple { background-position: -15000px 0px; }
603 | .emoji.s_pisces { background-position: -15025px 0px; }
604 | .emoji.s_pizza { background-position: -15050px 0px; }
605 | .emoji.s_point_down { background-position: -15075px 0px; }
606 | .emoji.s_point_left { background-position: -15100px 0px; }
607 | .emoji.s_point_right { background-position: -15125px 0px; }
608 | .emoji.s_point_up_2 { background-position: -15150px 0px; }
609 | .emoji.s_point_up { background-position: -15175px 0px; }
610 | .emoji.s_police_car { background-position: -15200px 0px; }
611 | .emoji.s_poodle { background-position: -15225px 0px; }
612 | .emoji.s_post_office { background-position: -15250px 0px; }
613 | .emoji.s_postal_horn { background-position: -15275px 0px; }
614 | .emoji.s_postbox { background-position: -15300px 0px; }
615 | .emoji.s_potable_water { background-position: -15325px 0px; }
616 | .emoji.s_pouch { background-position: -15350px 0px; }
617 | .emoji.s_poultry_leg { background-position: -15375px 0px; }
618 | .emoji.s_pound { background-position: -15400px 0px; }
619 | .emoji.s_pouting_cat { background-position: -15425px 0px; }
620 | .emoji.s_pray { background-position: -15450px 0px; }
621 | .emoji.s_princess { background-position: -15475px 0px; }
622 | .emoji.s_purple_heart { background-position: -15500px 0px; }
623 | .emoji.s_purse { background-position: -15525px 0px; }
624 | .emoji.s_pushpin { background-position: -15550px 0px; }
625 | .emoji.s_put_litter_in_its_place { background-position: -15575px 0px; }
626 | .emoji.s_question { background-position: -15600px 0px; }
627 | .emoji.s_rabbit { background-position: -15625px 0px; }
628 | .emoji.s_rabbit2 { background-position: -15650px 0px; }
629 | .emoji.s_racehorse { background-position: -15675px 0px; }
630 | .emoji.s_radio_button { background-position: -15700px 0px; }
631 | .emoji.s_radio { background-position: -15725px 0px; }
632 | .emoji.s_rage { background-position: -15750px 0px; }
633 | .emoji.s_rage1 { background-position: -15775px 0px; }
634 | .emoji.s_rage2 { background-position: -15800px 0px; }
635 | .emoji.s_rage3 { background-position: -15825px 0px; }
636 | .emoji.s_rage4 { background-position: -15850px 0px; }
637 | .emoji.s_railway_car { background-position: -15875px 0px; }
638 | .emoji.s_train { background-position: -15900px 0px; }
639 | .emoji.s_rainbow { background-position: -15925px 0px; }
640 | .emoji.s_raised_hands { background-position: -15950px 0px; }
641 | .emoji.s_raising_hand { background-position: -15975px 0px; }
642 | .emoji.s_ram { background-position: -16000px 0px; }
643 | .emoji.s_ramen { background-position: -16025px 0px; }
644 | .emoji.s_rat { background-position: -16050px 0px; }
645 | .emoji.s_recycle { background-position: -16075px 0px; }
646 | .emoji.s_red_circle { background-position: -16100px 0px; }
647 | .emoji.s_registered { background-position: -16125px 0px; }
648 | .emoji.s_relaxed { background-position: -16150px 0px; }
649 | .emoji.s_relieved { background-position: -16175px 0px; }
650 | .emoji.s_repeat_one { background-position: -16200px 0px; }
651 | .emoji.s_repeat { background-position: -16225px 0px; }
652 | .emoji.s_restroom { background-position: -16250px 0px; }
653 | .emoji.s_revolving_hearts { background-position: -16275px 0px; }
654 | .emoji.s_rewind { background-position: -16300px 0px; }
655 | .emoji.s_ribbon { background-position: -16325px 0px; }
656 | .emoji.s_rice_ball { background-position: -16350px 0px; }
657 | .emoji.s_rice_cracker { background-position: -16375px 0px; }
658 | .emoji.s_rice_scene { background-position: -16400px 0px; }
659 | .emoji.s_rice { background-position: -16425px 0px; }
660 | .emoji.s_ring { background-position: -16450px 0px; }
661 | .emoji.s_rocket { background-position: -16475px 0px; }
662 | .emoji.s_roller_coaster { background-position: -16500px 0px; }
663 | .emoji.s_rooster { background-position: -16525px 0px; }
664 | .emoji.s_rose { background-position: -16550px 0px; }
665 | .emoji.s_rotating_light { background-position: -16575px 0px; }
666 | .emoji.s_round_pushpin { background-position: -16600px 0px; }
667 | .emoji.s_rowboat { background-position: -16625px 0px; }
668 | .emoji.s_ru { background-position: -16650px 0px; }
669 | .emoji.s_rugby_football { background-position: -16675px 0px; }
670 | .emoji.s_runner { background-position: -16700px 0px; }
671 | .emoji.s_running { background-position: -16725px 0px; }
672 | .emoji.s_running_shirt_with_sash { background-position: -16750px 0px; }
673 | .emoji.s_sa { background-position: -16775px 0px; }
674 | .emoji.s_sagittarius { background-position: -16800px 0px; }
675 | .emoji.s_sake { background-position: -16825px 0px; }
676 | .emoji.s_sandal { background-position: -16850px 0px; }
677 | .emoji.s_santa { background-position: -16875px 0px; }
678 | .emoji.s_satellite { background-position: -16900px 0px; }
679 | .emoji.s_saxophone { background-position: -16925px 0px; }
680 | .emoji.s_school_satchel { background-position: -16950px 0px; }
681 | .emoji.s_school { background-position: -16975px 0px; }
682 | .emoji.s_scissors { background-position: -17000px 0px; }
683 | .emoji.s_scorpius { background-position: -17025px 0px; }
684 | .emoji.s_scream_cat { background-position: -17050px 0px; }
685 | .emoji.s_scream { background-position: -17075px 0px; }
686 | .emoji.s_scroll { background-position: -17100px 0px; }
687 | .emoji.s_seat { background-position: -17125px 0px; }
688 | .emoji.s_secret { background-position: -17150px 0px; }
689 | .emoji.s_see_no_evil { background-position: -17175px 0px; }
690 | .emoji.s_seedling { background-position: -17200px 0px; }
691 | .emoji.s_seven { background-position: -17225px 0px; }
692 | .emoji.s_shaved_ice { background-position: -17250px 0px; }
693 | .emoji.s_sheep { background-position: -17275px 0px; }
694 | .emoji.s_shell { background-position: -17300px 0px; }
695 | .emoji.s_ship { background-position: -17325px 0px; }
696 | .emoji.s_squirrel { background-position: -17350px 0px; }
697 | .emoji.s_shipit { background-position: -17375px 0px; }
698 | .emoji.s_tshirt { background-position: -17400px 0px; }
699 | .emoji.s_shirt { background-position: -17425px 0px; }
700 | .emoji.s_shower { background-position: -17450px 0px; }
701 | .emoji.s_signal_strength { background-position: -17475px 0px; }
702 | .emoji.s_six_pointed_star { background-position: -17500px 0px; }
703 | .emoji.s_six { background-position: -17525px 0px; }
704 | .emoji.s_ski { background-position: -17550px 0px; }
705 | .emoji.s_skull { background-position: -17575px 0px; }
706 | .emoji.s_sleeping { background-position: -17600px 0px; }
707 | .emoji.s_sleepy { background-position: -17625px 0px; }
708 | .emoji.s_slot_machine { background-position: -17650px 0px; }
709 | .emoji.s_small_blue_diamond { background-position: -17675px 0px; }
710 | .emoji.s_small_orange_diamond { background-position: -17700px 0px; }
711 | .emoji.s_small_red_triangle_down { background-position: -17725px 0px; }
712 | .emoji.s_small_red_triangle { background-position: -17750px 0px; }
713 | .emoji.s_smile_cat { background-position: -17775px 0px; }
714 | .emoji.s_smile { background-position: -17800px 0px; }
715 | .emoji.s_smiley_cat { background-position: -17825px 0px; }
716 | .emoji.s_smiley { background-position: -17850px 0px; }
717 | .emoji.s_smiling_imp { background-position: -17875px 0px; }
718 | .emoji.s_smirk_cat { background-position: -17900px 0px; }
719 | .emoji.s_smirk { background-position: -17925px 0px; }
720 | .emoji.s_smoking { background-position: -17950px 0px; }
721 | .emoji.s_snail { background-position: -17975px 0px; }
722 | .emoji.s_snake { background-position: -18000px 0px; }
723 | .emoji.s_snowboarder { background-position: -18025px 0px; }
724 | .emoji.s_snowflake { background-position: -18050px 0px; }
725 | .emoji.s_snowman { background-position: -18075px 0px; }
726 | .emoji.s_sob { background-position: -18100px 0px; }
727 | .emoji.s_soccer { background-position: -18125px 0px; }
728 | .emoji.s_soon { background-position: -18150px 0px; }
729 | .emoji.s_sos { background-position: -18175px 0px; }
730 | .emoji.s_sound { background-position: -18200px 0px; }
731 | .emoji.s_space_invader { background-position: -18225px 0px; }
732 | .emoji.s_spades { background-position: -18250px 0px; }
733 | .emoji.s_spaghetti { background-position: -18275px 0px; }
734 | .emoji.s_sparkle { background-position: -18300px 0px; }
735 | .emoji.s_sparkler { background-position: -18325px 0px; }
736 | .emoji.s_sparkles { background-position: -18350px 0px; }
737 | .emoji.s_sparkling_heart { background-position: -18375px 0px; }
738 | .emoji.s_speak_no_evil { background-position: -18400px 0px; }
739 | .emoji.s_speaker { background-position: -18425px 0px; }
740 | .emoji.s_speech_balloon { background-position: -18450px 0px; }
741 | .emoji.s_speedboat { background-position: -18475px 0px; }
742 | .emoji.s_star { background-position: -18500px 0px; }
743 | .emoji.s_star2 { background-position: -18525px 0px; }
744 | .emoji.s_stars { background-position: -18550px 0px; }
745 | .emoji.s_station { background-position: -18575px 0px; }
746 | .emoji.s_statue_of_liberty { background-position: -18600px 0px; }
747 | .emoji.s_steam_locomotive { background-position: -18625px 0px; }
748 | .emoji.s_stew { background-position: -18650px 0px; }
749 | .emoji.s_straight_ruler { background-position: -18675px 0px; }
750 | .emoji.s_strawberry { background-position: -18700px 0px; }
751 | .emoji.s_stuck_out_tongue_closed_eyes { background-position: -18725px 0px; }
752 | .emoji.s_stuck_out_tongue_winking_eye { background-position: -18750px 0px; }
753 | .emoji.s_stuck_out_tongue { background-position: -18775px 0px; }
754 | .emoji.s_sun_with_face { background-position: -18800px 0px; }
755 | .emoji.s_sunflower { background-position: -18825px 0px; }
756 | .emoji.s_sunglasses { background-position: -18850px 0px; }
757 | .emoji.s_sunny { background-position: -18875px 0px; }
758 | .emoji.s_sunrise_over_mountains { background-position: -18900px 0px; }
759 | .emoji.s_sunrise { background-position: -18925px 0px; }
760 | .emoji.s_surfer { background-position: -18950px 0px; }
761 | .emoji.s_sushi { background-position: -18975px 0px; }
762 | .emoji.s_suspect { background-position: -19000px 0px; }
763 | .emoji.s_suspension_railway { background-position: -19025px 0px; }
764 | .emoji.s_sweat_drops { background-position: -19050px 0px; }
765 | .emoji.s_sweat_smile { background-position: -19075px 0px; }
766 | .emoji.s_sweat { background-position: -19100px 0px; }
767 | .emoji.s_sweet_potato { background-position: -19125px 0px; }
768 | .emoji.s_swimmer { background-position: -19150px 0px; }
769 | .emoji.s_symbols { background-position: -19175px 0px; }
770 | .emoji.s_syringe { background-position: -19200px 0px; }
771 | .emoji.s_tada { background-position: -19225px 0px; }
772 | .emoji.s_tanabata_tree { background-position: -19250px 0px; }
773 | .emoji.s_tangerine { background-position: -19275px 0px; }
774 | .emoji.s_taurus { background-position: -19300px 0px; }
775 | .emoji.s_taxi { background-position: -19325px 0px; }
776 | .emoji.s_tea { background-position: -19350px 0px; }
777 | .emoji.s_telephone_receiver { background-position: -19375px 0px; }
778 | .emoji.s_telescope { background-position: -19400px 0px; }
779 | .emoji.s_tennis { background-position: -19425px 0px; }
780 | .emoji.s_tent { background-position: -19450px 0px; }
781 | .emoji.s_thought_balloon { background-position: -19475px 0px; }
782 | .emoji.s_three { background-position: -19500px 0px; }
783 | .emoji.s_ticket { background-position: -19525px 0px; }
784 | .emoji.s_tiger { background-position: -19550px 0px; }
785 | .emoji.s_tiger2 { background-position: -19575px 0px; }
786 | .emoji.s_tired_face { background-position: -19600px 0px; }
787 | .emoji.s_tm { background-position: -19625px 0px; }
788 | .emoji.s_toilet { background-position: -19650px 0px; }
789 | .emoji.s_tokyo_tower { background-position: -19675px 0px; }
790 | .emoji.s_tomato { background-position: -19700px 0px; }
791 | .emoji.s_tongue { background-position: -19725px 0px; }
792 | .emoji.s_top { background-position: -19750px 0px; }
793 | .emoji.s_tophat { background-position: -19775px 0px; }
794 | .emoji.s_tractor { background-position: -19800px 0px; }
795 | .emoji.s_traffic_light { background-position: -19825px 0px; }
796 | .emoji.s_train2 { background-position: -19850px 0px; }
797 | .emoji.s_tram { background-position: -19875px 0px; }
798 | .emoji.s_triangular_flag_on_post { background-position: -19900px 0px; }
799 | .emoji.s_triangular_ruler { background-position: -19925px 0px; }
800 | .emoji.s_trident { background-position: -19950px 0px; }
801 | .emoji.s_triumph { background-position: -19975px 0px; }
802 | .emoji.s_trolleybus { background-position: -20000px 0px; }
803 | .emoji.s_trollface { background-position: -20025px 0px; }
804 | .emoji.s_trophy { background-position: -20050px 0px; }
805 | .emoji.s_tropical_drink { background-position: -20075px 0px; }
806 | .emoji.s_tropical_fish { background-position: -20100px 0px; }
807 | .emoji.s_truck { background-position: -20125px 0px; }
808 | .emoji.s_trumpet { background-position: -20150px 0px; }
809 | .emoji.s_tulip { background-position: -20175px 0px; }
810 | .emoji.s_turtle { background-position: -20200px 0px; }
811 | .emoji.s_tv { background-position: -20225px 0px; }
812 | .emoji.s_twisted_rightwards_arrows { background-position: -20250px 0px; }
813 | .emoji.s_two_hearts { background-position: -20275px 0px; }
814 | .emoji.s_two_men_holding_hands { background-position: -20300px 0px; }
815 | .emoji.s_two_women_holding_hands { background-position: -20325px 0px; }
816 | .emoji.s_two { background-position: -20350px 0px; }
817 | .emoji.s_u6e80 { background-position: -20375px 0px; }
818 | .emoji.s_u7a7a { background-position: -20400px 0px; }
819 | .emoji.s_u55b6 { background-position: -20425px 0px; }
820 | .emoji.s_u5272 { background-position: -20450px 0px; }
821 | .emoji.s_u5408 { background-position: -20475px 0px; }
822 | .emoji.s_u6307 { background-position: -20500px 0px; }
823 | .emoji.s_u6708 { background-position: -20525px 0px; }
824 | .emoji.s_u6709 { background-position: -20550px 0px; }
825 | .emoji.s_u7121 { background-position: -20575px 0px; }
826 | .emoji.s_u7533 { background-position: -20600px 0px; }
827 | .emoji.s_u7981 { background-position: -20625px 0px; }
828 | .emoji.s_umbrella { background-position: -20650px 0px; }
829 | .emoji.s_unamused { background-position: -20675px 0px; }
830 | .emoji.s_underage { background-position: -20700px 0px; }
831 | .emoji.s_unlock { background-position: -20725px 0px; }
832 | .emoji.s_up { background-position: -20750px 0px; }
833 | .emoji.s_us { background-position: -20775px 0px; }
834 | .emoji.s_v { background-position: -20800px 0px; }
835 | .emoji.s_vertical_traffic_light { background-position: -20825px 0px; }
836 | .emoji.s_vhs { background-position: -20850px 0px; }
837 | .emoji.s_vibration_mode { background-position: -20875px 0px; }
838 | .emoji.s_video_camera { background-position: -20900px 0px; }
839 | .emoji.s_video_game { background-position: -20925px 0px; }
840 | .emoji.s_violin { background-position: -20950px 0px; }
841 | .emoji.s_virgo { background-position: -20975px 0px; }
842 | .emoji.s_volcano { background-position: -21000px 0px; }
843 | .emoji.s_vs { background-position: -21025px 0px; }
844 | .emoji.s_walking { background-position: -21050px 0px; }
845 | .emoji.s_waning_crescent_moon { background-position: -21075px 0px; }
846 | .emoji.s_waning_gibbous_moon { background-position: -21100px 0px; }
847 | .emoji.s_warning { background-position: -21125px 0px; }
848 | .emoji.s_watch { background-position: -21150px 0px; }
849 | .emoji.s_water_buffalo { background-position: -21175px 0px; }
850 | .emoji.s_watermelon { background-position: -21200px 0px; }
851 | .emoji.s_wave { background-position: -21225px 0px; }
852 | .emoji.s_wavy_dash { background-position: -21250px 0px; }
853 | .emoji.s_waxing_crescent_moon { background-position: -21275px 0px; }
854 | .emoji.s_wc { background-position: -21300px 0px; }
855 | .emoji.s_weary { background-position: -21325px 0px; }
856 | .emoji.s_wedding { background-position: -21350px 0px; }
857 | .emoji.s_whale { background-position: -21375px 0px; }
858 | .emoji.s_whale2 { background-position: -21400px 0px; }
859 | .emoji.s_wheelchair { background-position: -21425px 0px; }
860 | .emoji.s_white_check_mark { background-position: -21450px 0px; }
861 | .emoji.s_white_circle { background-position: -21475px 0px; }
862 | .emoji.s_white_flower { background-position: -21500px 0px; }
863 | .emoji.s_white_large_square { background-position: -21525px 0px; }
864 | .emoji.s_white_medium_small_square { background-position: -21550px 0px; }
865 | .emoji.s_white_medium_square { background-position: -21575px 0px; }
866 | .emoji.s_white_small_square { background-position: -21600px 0px; }
867 | .emoji.s_white_square_button { background-position: -21625px 0px; }
868 | .emoji.s_wind_chime { background-position: -21650px 0px; }
869 | .emoji.s_wine_glass { background-position: -21675px 0px; }
870 | .emoji.s_wink { background-position: -21700px 0px; }
871 | .emoji.s_wolf { background-position: -21725px 0px; }
872 | .emoji.s_woman { background-position: -21750px 0px; }
873 | .emoji.s_womans_clothes { background-position: -21775px 0px; }
874 | .emoji.s_womans_hat { background-position: -21800px 0px; }
875 | .emoji.s_womens { background-position: -21825px 0px; }
876 | .emoji.s_worried { background-position: -21850px 0px; }
877 | .emoji.s_wrench { background-position: -21875px 0px; }
878 | .emoji.s_x { background-position: -21900px 0px; }
879 | .emoji.s_yellow_heart { background-position: -21925px 0px; }
880 | .emoji.s_yen { background-position: -21950px 0px; }
881 | .emoji.s_yum { background-position: -21975px 0px; }
882 | .emoji.s_zap { background-position: -22000px 0px; }
883 | .emoji.s_zero { background-position: -22025px 0px; }
884 | .emoji.s_zzz { background-position: -22050px 0px; }
885 | .emoji.s_fu { background-position: -22075px 0px; }
886 |
--------------------------------------------------------------------------------
/src/emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin940726/minesweeper/ab9b0dd2d80e66389ed347455787934d0807799a/src/emoji.png
--------------------------------------------------------------------------------
/src/hasAppleColorEmoji.js:
--------------------------------------------------------------------------------
1 | /*
2 | * credits to @muanchiou
3 | * https://github.com/muan/emoji/blob/gh-pages/javascripts/hasAppleColorEmoji.js
4 | */
5 |
6 | const hasAppleColorEmoji = () => {
7 | var widths = [];
8 | var tags = [document.createElement('span'), document.createElement('span')];
9 | tags.forEach(function (tag, i) {
10 | tag.innerText = '⚙';
11 | tag.style.fontFamily = i === 1 ? 'thisisnotafont' : 'AppleColorEmoji';
12 | document.body.appendChild(tag);
13 | widths.push(tag.offsetWidth);
14 | document.body.removeChild(tag);
15 | });
16 | return (widths[0] !== widths[1]) || navigator.platform.match(/Mac/);
17 | };
18 |
19 | export default hasAppleColorEmoji;
20 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Minesweeper
8 |
9 |
10 |
11 |
12 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import { Provider } from 'react-redux';
4 | import thunk from 'redux-thunk';
5 | import { createStore, applyMiddleware, compose } from 'redux';
6 | import reducer from './reducers';
7 | import App from './components/App';
8 | import { changeMode } from './actions';
9 | import { StyleRoot } from 'radium';
10 | import Cookie from 'cookies-js';
11 |
12 | import Minesweeper from './minesweeper';
13 |
14 | const config = {
15 | isLoading: false,
16 | show: false,
17 | mode: false,
18 | flagMode: Cookie.get("flagMode") === "true" || false,
19 | checkIsSolvable: Cookie.get("checkIsSolvable") === "true" || false,
20 | rows: parseInt(Cookie.get("rows")) || 9,
21 | cols: parseInt(Cookie.get("cols")) || 9,
22 | mines: parseInt(Cookie.get("mines")) || 10
23 | };
24 |
25 | const mw = Minesweeper();
26 | mw.blocks = mw.reset(config.rows, config.cols, config.mines, config.flagMode, config.checkIsSolvable);
27 |
28 | const store = createStore(
29 | reducer, { mw, config, timePass: 0 },
30 | compose(
31 | applyMiddleware(thunk),
32 | window.devToolsExtension ? window.devToolsExtension() : f => f
33 | )
34 | );
35 |
36 | render(
37 |
38 |
39 |
40 |
41 | ,
42 | document.getElementById('root')
43 | );
44 |
--------------------------------------------------------------------------------
/src/minesweeper.js:
--------------------------------------------------------------------------------
1 | import Immutable, { Map, Record, List, Range } from 'immutable';
2 | import { EventEmitter } from 'events';
3 | import rref from './rref';
4 |
5 | export const BlockRecord = Record({ row: 0, col: 0 });
6 | export const Block = Record({
7 | type: 'normal',
8 | mines: 0,
9 | hidden: true,
10 | flag: false
11 | });
12 | /* create an instance of Immutable Map, append it's prototype. */
13 | export const Blocks = Map;
14 |
15 | /* get surrounding blocks, return a Map containing surrounding
16 | blocks except itself. */
17 | Blocks.prototype.getSurrounding = function(record) {
18 | return this.filter( (block, r) => (
19 | Math.abs(r.row - record.row) <= 1 &&
20 | Math.abs(r.col - record.col) <= 1 &&
21 | !(r.row === record.row && r.col === record.col)
22 | ));
23 | };
24 |
25 | /* initial the 'mines' of surrounding block of mine.
26 | return blocks. */
27 | Blocks.prototype.initSurrounding = function(record) {
28 | const block = this.get(record);
29 | return this.update(
30 | record,
31 | b => b.set("mines", block.mines + 1)
32 | );
33 | };
34 |
35 | /* open the blocks and the surrounding blocks recursively */
36 | Blocks.prototype.revealBlock = function(blockRecord, revealMineCallback) {
37 | // if click on mine, game lose, reveal all the mines.
38 | if (this.get(blockRecord).type === 'mine') {
39 | return revealMineCallback();
40 | }
41 | // click on normal hidden block
42 | else {
43 | let blocks = this.update(
44 | blockRecord,
45 | b => b.set("hidden", false)
46 | );
47 |
48 | if (!this.get(blockRecord).mines) {
49 | this.getSurrounding(blockRecord)
50 | .filter(block => block.hidden)
51 | .forEach( (block, record) => {
52 | blocks = blocks.revealBlock(record, revealMineCallback);
53 | });
54 | }
55 |
56 | return blocks;
57 | }
58 | };
59 |
60 | /* click on number, expand the surrounding blocks recursively */
61 | Blocks.prototype.expandBlock = function(blockRecord, revealMineCallback) {
62 | const block = this.get(blockRecord);
63 | let blocks = this;
64 |
65 | if (block.mines && this.getSurrounding(blockRecord).filter(b => b.flag).size === block.mines) {
66 | this.getSurrounding(blockRecord)
67 | .filter(b => b.hidden && !b.flag)
68 | .forEach( (b, record) => {
69 | blocks = blocks.revealBlock(record, revealMineCallback);
70 | });
71 | }
72 |
73 | return blocks;
74 | };
75 |
76 | /* set a flag on the block */
77 | Blocks.prototype.setFlag = function(blockRecord) {
78 | return this.update(
79 | blockRecord,
80 | b => b.set("flag", !b.flag)
81 | );
82 | };
83 |
84 | /* check if this blocks is game over */
85 | Blocks.prototype.checkGame = function() {
86 | if (this.filter(block => block.type === "normal")
87 | .every(block => !block.hidden)
88 | ) {
89 | return true;
90 | }
91 |
92 | return false;
93 | };
94 |
95 | /* random number generator */
96 | const RNG = ({
97 | start = 0,
98 | end,
99 | number = 1,
100 | exclude = List()
101 | }) => (
102 | Range(0, number)
103 | .reduce( (random, index) => {
104 | let r = Math.floor(Math.random() * (random.size - index));
105 | return random.push(random.get(r)).delete(r);
106 | }, Range(start, end).toList().filterNot(n => exclude.includes(n)))
107 | .takeLast(number)
108 | );
109 |
110 | /* main class */
111 | const Minesweeper = () => ({
112 | _timer: null,
113 | _eventEmitter: new EventEmitter(),
114 | on: function(event, callback) {
115 | return this._eventEmitter.on(event, callback);
116 | },
117 |
118 | /* reset the game board */
119 | reset: function(rows, cols, mines, flagMode, checkIsSolvable) {
120 | // reset variables
121 | this.rows = rows || 9;
122 | this.cols = cols || 9;
123 | this.mines = mines || 10;
124 | this.minesRemaining = this.mines;
125 | this.blocks = Blocks();
126 | this.status = "ready";
127 | this.timePass = 0;
128 | this.mode = "regular";
129 | this.flagMode = flagMode || false;
130 | this.checkIsSolvable = checkIsSolvable || false;
131 | clearInterval(this._timer);
132 | this._eventEmitter.emit("statuschanged", this.status);
133 |
134 | let blocks = this.blocks;
135 |
136 | // reset blocks
137 | Range(0, this.rows).forEach(row => (
138 | Range(0, this.cols).forEach(col => {
139 | blocks = blocks.set(
140 | new BlockRecord({ row, col }),
141 | new Block()
142 | );
143 | })
144 | ));
145 |
146 | return blocks;
147 | },
148 |
149 | /* initialize the game */
150 | init: function(rows, cols, mines, flagMode, checkIsSolvable, exclude = Map()) {
151 | let blocks = this.reset(rows, cols, mines, flagMode, checkIsSolvable);
152 |
153 | exclude = exclude.keySeq().map(record => record.row * this.cols + record.col);
154 |
155 | // initialize random mines
156 | RNG({ end: this.rows * this.cols, number: this.mines, exclude })
157 | .forEach(index => {
158 | const blockRecord = new BlockRecord({
159 | row: Math.floor(index / this.cols),
160 | col: index % this.cols
161 | });
162 |
163 | blocks = blocks.update(blockRecord, b => b.set("type", 'mine'));
164 |
165 | // initialize numbers of surronding mines.
166 | blocks.getSurrounding(blockRecord)
167 | .forEach( (block, record) => {
168 | blocks = blocks.initSurrounding(record);
169 | });
170 | });
171 |
172 | return blocks;
173 | },
174 |
175 | /* gameover, and reveal all the mines */
176 | revealMine: function() {
177 | this.status = "lose";
178 | this._eventEmitter.emit("statuschanged", this.status);
179 |
180 | clearInterval(this._timer);
181 |
182 | let blocks = this.blocks;
183 |
184 | blocks.keySeq()
185 | .toArray()
186 | .filter(key => blocks.get(key).type === "mine")
187 | .forEach(mine => {
188 | blocks = blocks.update(
189 | mine,
190 | b => b.set("hidden", false)
191 | );
192 | });
193 |
194 | return blocks;
195 | },
196 |
197 | /* reveal the block */
198 | clickOn: function(blockRecord) {
199 | let blocks = this.blocks;
200 |
201 | // first click, ensure no mines put in the surrounding of the clicked position.
202 | if (this.status === "ready") {
203 | const exclude = blocks.getSurrounding(blockRecord).set(blockRecord, blocks.get(blockRecord));
204 |
205 | blocks = this.init(this.rows, this.cols, this.mines, this.flagMode, this.checkIsSolvable, exclude);
206 | if (this.checkIsSolvable) {
207 | return this.solver(blocks.revealBlock(blockRecord))
208 | .then(solved => {
209 | if (!solved) {
210 | return this.clickOn(blockRecord);
211 | }
212 | else {
213 | this.status = "playing";
214 | this.mode = this.flagMode ? "quick" : "regular";
215 | this._eventEmitter.emit("statuschanged", this.status);
216 | this._timer = setInterval(() => {
217 | if (this.status === "playing") {
218 | this.timePass += 1;
219 | this._eventEmitter.emit("timeupdated", this.timePass);
220 | }
221 | }, 1000);
222 |
223 | return blocks.revealBlock(blockRecord);
224 | }
225 | });
226 | }
227 | else {
228 | this.status = "playing";
229 | this.mode = this.flagMode ? "quick" : "regular";
230 | this._eventEmitter.emit("statuschanged", this.status);
231 | this._timer = setInterval(() => {
232 | if (this.status === "playing") {
233 | this.timePass += 1;
234 | this._eventEmitter.emit("timeupdated", this.timePass);
235 | }
236 | }, 1000);
237 |
238 | return Promise.resolve(blocks.revealBlock(blockRecord));
239 | }
240 | }
241 |
242 | const block = blocks.get(blockRecord);
243 | // click on flag
244 | if (block.flag) {
245 | // do nothing
246 | }
247 | // click on hidden block
248 | else if (block.hidden) {
249 | if (block.type === 'mine') {
250 | blocks = this.revealMine();
251 | }
252 | else {
253 | blocks = blocks.revealBlock(blockRecord, () => this.revealMine());
254 | }
255 | }
256 | // click on number, expand surrounding block
257 | else if (!block.hidden) {
258 | blocks = blocks.expandBlock(blockRecord, () => this.revealMine());
259 | }
260 |
261 | this.checkGame(blocks);
262 |
263 | return Promise.resolve(blocks);
264 | },
265 |
266 | /* set flag to a block */
267 | rightClickOn: function(blockRecord) {
268 | let blocks = this.blocks;
269 | const block = blocks.get(blockRecord);
270 |
271 | if (block.hidden) {
272 | blocks = blocks.setFlag(blockRecord);
273 |
274 | this.minesRemaining += blocks.get(blockRecord).flag ? (this.minesRemaining <= 0 ? 0 : -1) : 1;
275 | }
276 | else {
277 | blocks = blocks.expandBlock(blockRecord, () => this.revealMine());
278 | }
279 |
280 | this.checkGame(blocks);
281 |
282 | return Promise.resolve(blocks);
283 | },
284 |
285 | /* perform single click,
286 | reveal block for regular mode,
287 | set flag for quick mode */
288 | singleClick: function(blockRecord) {
289 | if (this.status !== "win" && this.status !== "lose") {
290 | if (this.mode === "regular") {
291 | return this.clickOn(new BlockRecord(blockRecord));
292 | }
293 | else if (this.mode === "quick") {
294 | return this.rightClickOn(new BlockRecord(blockRecord));
295 | }
296 | }
297 |
298 | return Promise.resolve(this.blocks);
299 | },
300 | /* perform right click,
301 | reveal block for quick mode,
302 | set flag for regular mode */
303 | rightClick: function(blockRecord) {
304 | if (this.status !== "win" && this.status !== "lose") {
305 | if (this.mode === "regular") {
306 | return this.rightClickOn(new BlockRecord(blockRecord));
307 | }
308 | else if (this.mode === "quick") {
309 | return this.clickOn(new BlockRecord(blockRecord));
310 | }
311 | }
312 |
313 | return Promise.resolve(this.blocks);
314 | },
315 |
316 | /* check if game is over, emit the event */
317 | checkGame: function(blocks) {
318 | if (blocks.checkGame()) {
319 | this.status = "win";
320 | this._eventEmitter.emit("statuschanged", this.status);
321 | clearInterval(this._timer);
322 | return true;
323 | }
324 |
325 | return false;
326 | },
327 |
328 |
329 | /* --------------------- SOLVER --------------------- */
330 |
331 | /* get all the revealed blocks with number > 0
332 | and have surrounding hidden blocks */
333 | getEdgeBlockRecord: function(blocks) {
334 | return blocks
335 | .filter(block => block.type === "normal" && block.mines && !block.hidden)
336 | .filter( (block, record) =>
337 | blocks.getSurrounding(record).some(b => b.hidden && !b.flag)
338 | );
339 | },
340 | solveByRref: function(blocks, edges) {
341 | return new Promise(
342 | resolve => {
343 | /*
344 | * inspired by the great article by @ROBERT MASSAIOLI
345 | * https://massaioli.wordpress.com/2013/01/12/solving-minesweeper-with-matricies/
346 | */
347 | let changed = false;
348 |
349 | const edgeBlocks = blocks.filter(block => block.hidden).keySeq().toList();
350 |
351 | const matrix = edges.map( (block, edge) => {
352 | const hiddenBlock = blocks.getSurrounding(edge)
353 | .filter(b => b.hidden && !b.flag);
354 | return edgeBlocks.map(unknown => (
355 | hiddenBlock.find( (b, r) => Immutable.is(r, unknown)) ? 1 : 0
356 | )).toList().push(
357 | block.mines - blocks.getSurrounding(edge).filter(b => b.hidden && b.flag).size
358 | );
359 | }).toList();
360 |
361 | const remainBlocks = blocks.filter(block => block.hidden && !block.flag).keySeq().toList();
362 |
363 | const remainCondition = List([
364 | ...edgeBlocks.map(record =>
365 | remainBlocks.find(r => Immutable.is(record, r) ) ? 1 : 0
366 | ),
367 | this.mines - blocks.filter(block => block.flag && block.hidden).size
368 | ]);
369 |
370 | const rrefMatrix = rref(matrix.push(remainCondition));
371 |
372 | const bounds = rrefMatrix.map(row =>
373 | row.slice(0, row.size - 1)
374 | .reduce( (bound, col) => (
375 | List([
376 | bound.get(0) + (col === 1 ? 1 : 0), // maximum bound
377 | bound.get(1) + (col === -1 ? -1 : 0) // minimum bound
378 | ])
379 | ), List([0, 0]))
380 | .push(row.last()) // concat with the augmented column
381 | );
382 |
383 | bounds.forEach( (bound, row) => {
384 | if (bound.get(0) === bound.get(2) || bound.get(1) === bound.get(2)) {
385 | const boundCondition = bound.get(0) === bound.get(2) ? 1 : -1;
386 | rrefMatrix.get(row).slice(0, rrefMatrix.get(row).size - 1)
387 | .forEach( (col, i) => {
388 | if (col === boundCondition) {
389 | // console.log('flag', edgeBlocks[i]);
390 | blocks = blocks.update(edgeBlocks.get(i), b => b.set("flag", true));
391 | changed = true;
392 | }
393 | else if (col === -boundCondition) {
394 | // console.log('reveal', edgeBlocks[i]);
395 | blocks = blocks.revealBlock(edgeBlocks.get(i));
396 | changed = true;
397 | }
398 | });
399 | }
400 | });
401 |
402 | if (!blocks.checkGame()) {
403 | if (changed) {
404 | setTimeout(() => {
405 | resolve(this.solver(blocks));
406 | }, 0);
407 | }
408 | else {
409 | resolve(false);
410 | }
411 | }
412 | else {
413 | resolve(true);
414 | }
415 | }
416 | );
417 | },
418 |
419 | solver: function(blocks) {
420 | return new Promise(
421 | resolve => {
422 | const edges = this.getEdgeBlockRecord(blocks);
423 | let changed = false;
424 |
425 | // set flag to those satisfy the mines number
426 | edges.forEach( (block, record) => {
427 | const surroundingHidden = blocks.getSurrounding(record)
428 | .filter(b => b.hidden);
429 |
430 | if (block.mines === surroundingHidden.size) {
431 | surroundingHidden.forEach( (b, r) => {
432 | blocks = blocks.update(
433 | r,
434 | b => b.set("flag", true)
435 | );
436 | });
437 | changed = true;
438 | }
439 | });
440 |
441 | // reveal those edge blocks being set flag.
442 | edges.forEach( (block, record) => {
443 | const surrounding = blocks.getSurrounding(record);
444 |
445 | if (surrounding.filter(b => b.hidden && b.flag).size === block.mines) {
446 | surrounding.filter(b => b.hidden && !b.flag)
447 | .forEach( (b, r) => {
448 | blocks = blocks.revealBlock(r);
449 | });
450 | changed = true;
451 | }
452 | });
453 |
454 | if (changed) {
455 | setTimeout(() => {
456 | resolve(this.solver(blocks));
457 | }, 0);
458 | }
459 | else {
460 | if (!blocks.checkGame()) {
461 | setTimeout(() => {
462 | resolve(this.solveByRref(blocks, edges));
463 | }, 0);
464 | }
465 | else {
466 | resolve(true);
467 | }
468 | }
469 | }
470 | );
471 | }
472 | });
473 |
474 | export default Minesweeper;
475 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions';
2 |
3 | const reducers = handleActions({
4 | SET_GAME: (state, action) => ({
5 | ...state,
6 | mw: {
7 | ...state.mw,
8 | blocks: action.payload
9 | }
10 | }),
11 |
12 | TOGGLE_LOADING: (state) => ({
13 | ...state,
14 | config: {
15 | ...state.config,
16 | isLoading: !state.config.isLoading
17 | }
18 | }),
19 |
20 | TOGGLE_MODE: (state) => {
21 | state.mw.mode = state.mw.mode === "regular" ? "quick" : "regular";
22 |
23 | return {
24 | ...state,
25 | mw: state.mw
26 | };
27 | },
28 |
29 | RESTART_GAME: (state) => ({
30 | ...state,
31 | mw: {
32 | ...state.mw,
33 | blocks: state.mw.reset(state.config.rows, state.config.cols, state.config.mines, state.config.flagMode, state.config.checkIsSolvable)
34 | },
35 | timePass: 0
36 | }),
37 |
38 | UPDATE_TIME: (state, action) => ({
39 | ...state,
40 | timePass: action.payload
41 | }),
42 |
43 | SAVE_CONFIG: (state, action) => ({
44 | ...state,
45 | config: {
46 | ...state.config,
47 | [action.payload.target]: action.payload.value
48 | }
49 | }),
50 |
51 | TOGGLE_PANEL: (state) => ({
52 | ...state,
53 | config: {
54 | ...state.config,
55 | show: !state.config.show
56 | }
57 | }),
58 |
59 | TOGGLE_FLAG_MODE: (state) => {
60 | state.mw.flagMode = !state.mw.flagMode;
61 | return {
62 | ...state,
63 | mw: state.mw,
64 | config: {
65 | ...state.config,
66 | flagMode: state.mw.flagMode
67 | }
68 | };
69 | },
70 | TOGGLE_CHECK_IS_SOLVABLE: (state) => {
71 | state.mw.checkIsSolvable = !state.mw.checkIsSolvable;
72 | return {
73 | ...state,
74 | mw: state.mw,
75 | config: {
76 | ...state.config,
77 | checkIsSolvable: state.mw.checkIsSolvable
78 | }
79 | };
80 | }
81 | });
82 |
83 | export default reducers;
84 |
--------------------------------------------------------------------------------
/src/rref.js:
--------------------------------------------------------------------------------
1 | import { fromJS, List, Range, is } from 'immutable';
2 |
3 | const rref = (matrix) => {
4 | const m = List.isList(matrix) ? matrix : fromJS(matrix);
5 |
6 | let A = m;
7 | const rows = A.size;
8 | const columns = A.get(0).size;
9 |
10 | let lead = 0;
11 | for (let k = 0; k < rows; k++) {
12 | if (columns <= lead) {
13 | return A;
14 | }
15 |
16 | let i = k;
17 | while (A.get(i).get(lead) === 0) {
18 | i++;
19 | if (rows === i) {
20 | i = k;
21 | lead++;
22 | if (columns === lead) {
23 | return A;
24 | }
25 | }
26 | }
27 |
28 | const irow = A.get(i);
29 | const krow = A.get(k);
30 | A = A.update(i, r => krow);
31 | A = A.update(k, r => irow);
32 |
33 | let val = A.get(k).get(lead);
34 | for (let j = 0; j < columns; j++) {
35 | A = A.update(k, r => r.update(j, c => c / val));
36 | }
37 |
38 | for (let i = 0; i < rows; i++) {
39 | if (i === k) continue;
40 | val = A.get(i).get(lead);
41 | for (let j = 0; j < columns; j++) {
42 | A = A.update(i, r => r.update(j, c => c - val * A.get(k).get(j)));
43 | }
44 | }
45 | lead++;
46 | }
47 |
48 | return A;
49 | };
50 |
51 | export default rref;
52 |
--------------------------------------------------------------------------------
/src/test.js:
--------------------------------------------------------------------------------
1 | import Minesweeper, { BlockRecord } from './minesweeper';
2 |
3 | const mw = Minesweeper();
4 | const iterate = 100;
5 |
6 | const counter = {
7 | win: 0,
8 | lose: 0
9 | };
10 |
11 | const now = new Date();
12 |
13 | const testset = new Array(iterate).fill(0).map(() => {
14 | mw.blocks = mw.reset(16, 16, 40, true, false);
15 | counter[mw.solver(
16 | mw.clickOn(new BlockRecord({ row: 4, col: 4}))
17 | ) ? 'win' : 'lose'] += 1;
18 | });
19 |
20 | const passtime = new Date() - now;
21 |
22 | console.log('---------------------------------');
23 | console.log('time: ', passtime);
24 | console.log(counter);
25 | console.log('---------------------------------');
26 | console.log('avg time: ', passtime / iterate);
27 | console.log(counter.win / iterate * 100 + '%');
28 | console.log('---------------------------------');
29 | console.log('waiting time: ', passtime / counter.win );
30 | console.log('---------------------------------');
31 |
--------------------------------------------------------------------------------
/test/actions/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin940726/minesweeper/ab9b0dd2d80e66389ed347455787934d0807799a/test/actions/.keep
--------------------------------------------------------------------------------
/test/components/MainTest.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node, mocha */
2 | /*global expect */
3 | /*eslint no-console: 0*/
4 | 'use strict';
5 |
6 | // Uncomment the following lines to use the react test utilities
7 | // import React from 'react/addons';
8 | // const TestUtils = React.addons.TestUtils;
9 | import createComponent from 'helpers/shallowRenderHelper';
10 |
11 | import Main from 'components/Main';
12 |
13 | describe('MainComponent', () => {
14 | let MainComponent;
15 |
16 | beforeEach(() => {
17 | MainComponent = createComponent(Main);
18 | });
19 |
20 | it('should have its component name as default className', () => {
21 | expect(MainComponent.props.className).to.equal('index');
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/config/ConfigTest.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node, mocha */
2 | /*global expect */
3 | /*eslint no-console: 0*/
4 | 'use strict';
5 |
6 | import config from 'config';
7 |
8 | describe('appEnvConfigTests', () => {
9 | it('should load app config file depending on current --env', () => {
10 | expect(config.appEnv).to.equal('test');
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/test/helpers/shallowRenderHelper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Function to get the shallow output for a given component
3 | * As we are using phantom.js, we also need to include the fn.proto.bind shim!
4 | *
5 | * @see http://simonsmith.io/unit-testing-react-components-without-a-dom/
6 | * @author somonsmith
7 | */
8 | import React from 'react';
9 | import TestUtils from 'react-addons-test-utils';
10 |
11 | /**
12 | * Get the shallow rendered component
13 | *
14 | * @param {Object} component The component to return the output for
15 | * @param {Object} props [optional] The components properties
16 | * @param {Mixed} ...children [optional] List of children
17 | * @return {Object} Shallow rendered output
18 | */
19 | export default function createComponent(component, props = {}, ...children) {
20 | const shallowRenderer = TestUtils.createRenderer();
21 | shallowRenderer.render(React.createElement(component, props, children.length > 1 ? children : children[0]));
22 | return shallowRenderer.getRenderOutput();
23 | }
24 |
--------------------------------------------------------------------------------
/test/loadtests.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('babel-polyfill');
4 | require('core-js/fn/object/assign');
5 |
6 | // Add support for all files in the test directory
7 | const testsContext = require.context('.', true, /(Test\.js$)|(Helper\.js$)/);
8 | testsContext.keys().forEach(testsContext);
9 |
--------------------------------------------------------------------------------
/test/sources/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin940726/minesweeper/ab9b0dd2d80e66389ed347455787934d0807799a/test/sources/.keep
--------------------------------------------------------------------------------
/test/stores/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin940726/minesweeper/ab9b0dd2d80e66389ed347455787934d0807799a/test/stores/.keep
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const args = require('minimist')(process.argv.slice(2));
5 |
6 | // List of allowed environments
7 | const allowedEnvs = ['dev', 'dist', 'test'];
8 |
9 | // Set the correct environment
10 | var env;
11 | if (args._.length > 0 && args._.indexOf('start') !== -1) {
12 | env = 'test';
13 | } else if (args.env) {
14 | env = args.env;
15 | } else {
16 | env = 'dev';
17 | }
18 | process.env.REACT_WEBPACK_ENV = env;
19 |
20 | /**
21 | * Build the webpack configuration
22 | * @param {String} wantedEnv The wanted environment
23 | * @return {Object} Webpack config
24 | */
25 | function buildConfig(wantedEnv) {
26 | let isValid = wantedEnv && wantedEnv.length > 0 && allowedEnvs.indexOf(wantedEnv) !== -1;
27 | let validEnv = isValid ? wantedEnv : 'dev';
28 | let config = require(path.join(__dirname, 'cfg/' + validEnv));
29 | return config;
30 | }
31 |
32 | module.exports = buildConfig(env);
33 |
--------------------------------------------------------------------------------