├── .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 | ![screenshot](screenshot.png) 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 |
46 | 47 | 48 | {mode === "regular" ? (🖱) : (🚩)} 49 | 50 |
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 |
105 |
106 | 110 | 114 |
115 | saveAllConfig(9, 9, 10)}>🌱 116 | saveAllConfig(16, 16, 40)}>☘ 117 | saveAllConfig(16, 30, 99)}>🍀 118 |
119 | 123 | 127 | 131 | 132 | 135 |
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 |
95 |

96 | 💣 {minesRemaining} 97 |

98 | 102 | 103 | {text === "grin" ? "😁" : (text === "cry" ? "😢" : "😊")} 104 | 105 | 106 | 107 |
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 | --------------------------------------------------------------------------------